mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-15 12:51:00 +02:00
Add support for blinded addresses
This commit is contained in:
parent
00fba7771c
commit
17a22f1495
6 changed files with 154 additions and 0 deletions
|
|
@ -566,6 +566,8 @@ class CCustomParams : public CRegTestParams {
|
|||
base58Prefixes[PARENT_SCRIPT_ADDRESS] = std::vector<unsigned char>(1, args.GetArg("-parentscriptprefix", 196));
|
||||
parent_bech32_hrp = args.GetArg("-parent_bech32_hrp", "bcrt");
|
||||
|
||||
base58Prefixes[BLINDED_ADDRESS] = std::vector<unsigned char>(1, args.GetArg("-blindedprefix", 4));
|
||||
|
||||
// END ELEMENTS fields
|
||||
|
||||
// CSV always active by default, unlike regtest
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ public:
|
|||
EXT_PUBLIC_KEY,
|
||||
EXT_SECRET_KEY,
|
||||
// ELEMENTS
|
||||
BLINDED_ADDRESS,
|
||||
PARENT_PUBKEY_ADDRESS,
|
||||
PARENT_SCRIPT_ADDRESS,
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,17 @@ public:
|
|||
|
||||
std::string operator()(const PKHash& id) const
|
||||
{
|
||||
if (id.blinding_pubkey.IsFullyValid()) {
|
||||
assert(!for_parent);
|
||||
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::BLINDED_ADDRESS);
|
||||
// Blinded addresses have the actual address type prefix inside the payload.
|
||||
std::vector<unsigned char> prefix = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
|
||||
data.insert(data.end(), prefix.begin(), prefix.end());
|
||||
data.insert(data.end(), id.blinding_pubkey.begin(), id.blinding_pubkey.end());
|
||||
data.insert(data.end(), id.begin(), id.end());
|
||||
return EncodeBase58Check(data);
|
||||
}
|
||||
|
||||
CChainParams::Base58Type type = for_parent ? CChainParams::PARENT_PUBKEY_ADDRESS : CChainParams::PUBKEY_ADDRESS;
|
||||
std::vector<unsigned char> data = m_params.Base58Prefix(type);
|
||||
data.insert(data.end(), id.begin(), id.end());
|
||||
|
|
@ -39,6 +50,17 @@ public:
|
|||
|
||||
std::string operator()(const ScriptHash& id) const
|
||||
{
|
||||
if (id.blinding_pubkey.IsFullyValid()) {
|
||||
assert(!for_parent);
|
||||
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::BLINDED_ADDRESS);
|
||||
// Blinded addresses have the actual address type prefix inside the payload.
|
||||
std::vector<unsigned char> prefix = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
|
||||
data.insert(data.end(), prefix.begin(), prefix.end());
|
||||
data.insert(data.end(), id.blinding_pubkey.begin(), id.blinding_pubkey.end());
|
||||
data.insert(data.end(), id.begin(), id.end());
|
||||
return EncodeBase58Check(data);
|
||||
}
|
||||
|
||||
CChainParams::Base58Type type = for_parent ? CChainParams::PARENT_SCRIPT_ADDRESS : CChainParams::SCRIPT_ADDRESS;
|
||||
std::vector<unsigned char> data = m_params.Base58Prefix(type);
|
||||
data.insert(data.end(), id.begin(), id.end());
|
||||
|
|
@ -82,17 +104,31 @@ public:
|
|||
CTxDestination DecodeDestination(const std::string& str, const CChainParams& params, const bool for_parent)
|
||||
{
|
||||
std::vector<unsigned char> data;
|
||||
size_t pk_size = CPubKey::COMPRESSED_PUBLIC_KEY_SIZE;
|
||||
uint160 hash;
|
||||
if (DecodeBase58Check(str, data)) {
|
||||
// base58-encoded Bitcoin addresses.
|
||||
// Public-key-hash-addresses have version 0 (or 111 testnet).
|
||||
// The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
|
||||
|
||||
// Blinded addresses have two prefixes: first the blinded one, then the traditional one.
|
||||
const std::vector<unsigned char>& blinded_prefix = params.Base58Prefix(CChainParams::BLINDED_ADDRESS);
|
||||
|
||||
CChainParams::Base58Type type_pkh = for_parent ? CChainParams::PARENT_PUBKEY_ADDRESS : CChainParams::PUBKEY_ADDRESS;
|
||||
const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(type_pkh);
|
||||
if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) {
|
||||
std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin());
|
||||
return PKHash(hash);
|
||||
} else if (data.size() == hash.size() + blinded_prefix.size() + pubkey_prefix.size() + pk_size &&
|
||||
std::equal(blinded_prefix.begin(), blinded_prefix.end(), data.begin()) &&
|
||||
std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin() + blinded_prefix.size())) {
|
||||
auto payload_start = data.begin() + blinded_prefix.size() + pubkey_prefix.size();
|
||||
CPubKey pubkey;
|
||||
pubkey.Set(payload_start, payload_start + pk_size);
|
||||
std::copy(payload_start + pk_size, data.end(), hash.begin());
|
||||
return PKHash(hash, pubkey);
|
||||
}
|
||||
|
||||
// Script-hash-addresses have version 5 (or 196 testnet).
|
||||
// The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
|
||||
CChainParams::Base58Type type_sh = for_parent ? CChainParams::PARENT_SCRIPT_ADDRESS : CChainParams::SCRIPT_ADDRESS;
|
||||
|
|
@ -100,6 +136,14 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
|
|||
if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) {
|
||||
std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin());
|
||||
return ScriptHash(hash);
|
||||
} else if (data.size() == hash.size() + blinded_prefix.size() + pubkey_prefix.size() + pk_size &&
|
||||
std::equal(blinded_prefix.begin(), blinded_prefix.end(), data.begin()) &&
|
||||
std::equal(script_prefix.begin(), script_prefix.end(), data.begin() + blinded_prefix.size())) {
|
||||
auto payload_start = data.begin() + blinded_prefix.size() + script_prefix.size();
|
||||
CPubKey pubkey;
|
||||
pubkey.Set(payload_start, payload_start + pk_size);
|
||||
std::copy(payload_start + pk_size, data.end(), hash.begin());
|
||||
return ScriptHash(hash, pubkey);
|
||||
}
|
||||
}
|
||||
data.clear();
|
||||
|
|
|
|||
|
|
@ -543,6 +543,88 @@ UniValue calcfastmerkleroot(const JSONRPCRequest& request)
|
|||
}
|
||||
|
||||
|
||||
class BlindingPubkeyAdderVisitor : public boost::static_visitor<>
|
||||
{
|
||||
public:
|
||||
CPubKey blind_key;
|
||||
explicit BlindingPubkeyAdderVisitor(const CPubKey& blind_key_in) : blind_key(blind_key_in) {}
|
||||
|
||||
void operator()(const CNoDestination& dest) const {}
|
||||
|
||||
void operator()(PKHash& keyID) const
|
||||
{
|
||||
keyID.blinding_pubkey = blind_key;
|
||||
}
|
||||
|
||||
void operator()(ScriptHash& scriptID) const
|
||||
{
|
||||
scriptID.blinding_pubkey = blind_key;
|
||||
}
|
||||
|
||||
void operator()(WitnessV0KeyHash& id) const
|
||||
{
|
||||
id.blinding_pubkey = blind_key;
|
||||
}
|
||||
|
||||
void operator()(WitnessV0ScriptHash& id) const
|
||||
{
|
||||
id.blinding_pubkey = blind_key;
|
||||
}
|
||||
|
||||
void operator()(WitnessUnknown& id) const
|
||||
{
|
||||
id.blinding_pubkey = blind_key;
|
||||
}
|
||||
|
||||
void operator()(const NullData& id) const {}
|
||||
};
|
||||
|
||||
|
||||
UniValue createblindedaddress(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 2)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"createblindedaddress address blinding_key\n"
|
||||
"\nCreates a blinded address using the provided blinding key.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"address\" (string, required) The unblinded address to be blinded.\n"
|
||||
"2. \"blinding_key\" (string, required) The blinding public key. This can be obtained for a given address using `validateaddress`.\n"
|
||||
"\nResult:\n"
|
||||
"\"blinded_address\" (string) The blinded address.\n"
|
||||
"\nExamples:\n"
|
||||
"\nCreate a multisig address from 2 addresses\n"
|
||||
+ HelpExampleCli("createblindedaddress", "HEZk3iQi1jC49bxUriTtynnXgWWWdAYx16 ec09811118b6febfa5ebe68642e5091c418fbace07e655da26b4a845a691fc2d") +
|
||||
"\nAs a json rpc call\n"
|
||||
+ HelpExampleRpc("createblindedaddress", "HEZk3iQi1jC49bxUriTtynnXgWWWdAYx16, ec09811118b6febfa5ebe68642e5091c418fbace07e655da26b4a845a691fc2d")
|
||||
);
|
||||
}
|
||||
|
||||
CTxDestination address = DecodeDestination(request.params[0].get_str());
|
||||
if (!IsValidDestination(address)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
|
||||
}
|
||||
if (IsBlindDestination(address)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not an unblinded address");
|
||||
}
|
||||
|
||||
if (!IsHex(request.params[1].get_str())) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal for key");
|
||||
}
|
||||
std::vector<unsigned char> keydata = ParseHex(request.params[1].get_str());
|
||||
if (keydata.size() != 33) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal key length, must be length 66.");
|
||||
}
|
||||
|
||||
CPubKey key;
|
||||
key.Set(keydata.begin(), keydata.end());
|
||||
|
||||
// Append blinding key and return
|
||||
boost::apply_visitor(BlindingPubkeyAdderVisitor(key), address);
|
||||
return EncodeDestination(address);
|
||||
}
|
||||
|
||||
|
||||
// END ELEMENTS CALLS
|
||||
//
|
||||
|
||||
|
|
@ -560,6 +642,7 @@ static const CRPCCommand commands[] =
|
|||
// ELEMENTS:
|
||||
{ "util", "getpakinfo", &getpakinfo, {}},
|
||||
{ "util", "tweakfedpegscript", &tweakfedpegscript, {"claim_script"} },
|
||||
{ "util", "createblindedaddress", &createblindedaddress, {"address", "blinding_key"}},
|
||||
{ "hidden", "calcfastmerkleroot", &calcfastmerkleroot, {"leaves"} },
|
||||
|
||||
/* Not shown in help */
|
||||
|
|
|
|||
|
|
@ -22,14 +22,25 @@ unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY;
|
|||
CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
|
||||
|
||||
ScriptHash::ScriptHash(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
|
||||
ScriptHash::ScriptHash(const CScript& in, const CPubKey& blinding_pubkey_in) : uint160(Hash160(in.begin(), in.end())), blinding_pubkey(blinding_pubkey_in) {}
|
||||
ScriptHash::ScriptHash(const uint160& hash, const CPubKey& blinding_pubkey_in) : uint160(hash), blinding_pubkey(blinding_pubkey_in) {}
|
||||
|
||||
PKHash::PKHash(const CPubKey& pubkey) : uint160(pubkey.GetID()) {}
|
||||
PKHash::PKHash(const CPubKey& pubkey, const CPubKey& blinding_pubkey_in) : uint160(pubkey.GetID()), blinding_pubkey(blinding_pubkey_in) {}
|
||||
PKHash::PKHash(const uint160& hash, const CPubKey& blinding_pubkey_in) : uint160(hash), blinding_pubkey(blinding_pubkey_in) {}
|
||||
|
||||
WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in)
|
||||
{
|
||||
CSHA256().Write(in.data(), in.size()).Finalize(begin());
|
||||
}
|
||||
|
||||
WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in, const CPubKey& blinding_pubkey_in)
|
||||
{
|
||||
CSHA256().Write(in.data(), in.size()).Finalize(begin());
|
||||
blinding_pubkey = blinding_pubkey_in;
|
||||
}
|
||||
|
||||
|
||||
const char* GetTxnOutputType(txnouttype t)
|
||||
{
|
||||
switch (t)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <pubkey.h> // blinding_pubkey
|
||||
|
||||
static const bool DEFAULT_ACCEPT_DATACARRIER = true;
|
||||
|
||||
class CKeyID;
|
||||
|
|
@ -79,7 +81,10 @@ struct PKHash : public uint160
|
|||
PKHash() : uint160() {}
|
||||
explicit PKHash(const uint160& hash) : uint160(hash) {}
|
||||
explicit PKHash(const CPubKey& pubkey);
|
||||
explicit PKHash(const CPubKey& pubkey, const CPubKey& blinding_pubkey);
|
||||
explicit PKHash(const uint160& hash, const CPubKey& blinding_pubkey);
|
||||
using uint160::uint160;
|
||||
CPubKey blinding_pubkey;
|
||||
};
|
||||
|
||||
struct ScriptHash : public uint160
|
||||
|
|
@ -87,7 +92,10 @@ struct ScriptHash : public uint160
|
|||
ScriptHash() : uint160() {}
|
||||
explicit ScriptHash(const uint160& hash) : uint160(hash) {}
|
||||
explicit ScriptHash(const CScript& script);
|
||||
explicit ScriptHash(const CScript& script, const CPubKey& blinding_pubkey);
|
||||
explicit ScriptHash(const uint160& hash, const CPubKey& blinding_pubkey);
|
||||
using uint160::uint160;
|
||||
CPubKey blinding_pubkey;
|
||||
};
|
||||
|
||||
struct WitnessV0ScriptHash : public uint256
|
||||
|
|
@ -95,14 +103,18 @@ struct WitnessV0ScriptHash : public uint256
|
|||
WitnessV0ScriptHash() : uint256() {}
|
||||
explicit WitnessV0ScriptHash(const uint256& hash) : uint256(hash) {}
|
||||
explicit WitnessV0ScriptHash(const CScript& script);
|
||||
explicit WitnessV0ScriptHash(const CScript& script, const CPubKey& blinding_pubkey);
|
||||
using uint256::uint256;
|
||||
CPubKey blinding_pubkey;
|
||||
};
|
||||
|
||||
struct WitnessV0KeyHash : public uint160
|
||||
{
|
||||
WitnessV0KeyHash() : uint160() {}
|
||||
explicit WitnessV0KeyHash(const uint160& hash) : uint160(hash) {}
|
||||
explicit WitnessV0KeyHash(const uint160& hash, const CPubKey& blinding_pubkey_in) : uint160(hash), blinding_pubkey(blinding_pubkey_in) {}
|
||||
using uint160::uint160;
|
||||
CPubKey blinding_pubkey;
|
||||
};
|
||||
|
||||
//! CTxDestination subtype to encode any future Witness version
|
||||
|
|
@ -111,6 +123,7 @@ struct WitnessUnknown
|
|||
unsigned int version;
|
||||
unsigned int length;
|
||||
unsigned char program[40];
|
||||
CPubKey blinding_pubkey;
|
||||
|
||||
friend bool operator==(const WitnessUnknown& w1, const WitnessUnknown& w2) {
|
||||
if (w1.version != w2.version) return false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue