Add support for blinded addresses

This commit is contained in:
Steven Roose 2019-03-15 18:05:43 +00:00
parent 00fba7771c
commit 17a22f1495
No known key found for this signature in database
GPG key ID: 7FC91380BB4CE800
6 changed files with 154 additions and 0 deletions

View file

@ -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

View file

@ -54,6 +54,7 @@ public:
EXT_PUBLIC_KEY,
EXT_SECRET_KEY,
// ELEMENTS
BLINDED_ADDRESS,
PARENT_PUBKEY_ADDRESS,
PARENT_SCRIPT_ADDRESS,

View file

@ -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();

View file

@ -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 */

View file

@ -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)

View file

@ -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;