Skip to content

Commit

Permalink
Add support for blinded addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
stevenroose committed Mar 20, 2019
1 parent 00fba77 commit 17a22f1
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class CChainParams
EXT_PUBLIC_KEY,
EXT_SECRET_KEY,
// ELEMENTS
BLINDED_ADDRESS,
PARENT_PUBKEY_ADDRESS,
PARENT_SCRIPT_ADDRESS,

Expand Down
44 changes: 44 additions & 0 deletions src/key_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ class DestinationEncoder : public boost::static_visitor<std::string>

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());
Expand All @@ -39,6 +50,17 @@ class DestinationEncoder : public boost::static_visitor<std::string>

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());
Expand Down Expand Up @@ -82,24 +104,46 @@ class DestinationEncoder : public boost::static_visitor<std::string>
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;
const std::vector<unsigned char>& script_prefix = params.Base58Prefix(type_sh);
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();
Expand Down
83 changes: 83 additions & 0 deletions src/rpc/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
//

Expand All @@ -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 */
Expand Down
11 changes: 11 additions & 0 deletions src/script/standard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 13 additions & 0 deletions src/script/standard.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

#include <stdint.h>

#include <pubkey.h> // blinding_pubkey

static const bool DEFAULT_ACCEPT_DATACARRIER = true;

class CKeyID;
Expand Down Expand Up @@ -79,30 +81,40 @@ 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
{
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
{
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
Expand All @@ -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;
Expand Down

0 comments on commit 17a22f1

Please sign in to comment.