Auto merge of #1026 - ebfull:address-serialization, r=ebfull

Zcash address encoding

We need to encode Zcash addresses so they aren't as large and unweildy. We're using Base58Check just like upstream does, and to ensure the first character is "z" in our addresses we must use two bytes for the version string. Two bytes gives us an extra character for free, so this PR targets the beginning of addresses to have "zc".

```
$ ./src/zcash-cli zcrawkeygen
{
    "zcaddress" : "tnvaj4ZbZG83tj4RwZcFeLgJoSt8nw1ZvSCG8EMyowAsXTQgJPat77Y43BVdVCrwrbLy7GG9msJDYdn5hmreHmkXAkX17hb",
    "zcsecretkey" : "SKzkxCRWvscKnroSFyhCqhY332KcDMH4LLNdK2TsSvbmr3CGAB8B",
    "zcviewingkey" : "10aa74046f31cbe5eaa8965d1e104853234c3d6c6e45f9c497ca3a025d159755"
}
```

This PR also encodes the spending keys with a prefix that targets "SK". The spec needs to be updated with these changes.

Testnet addresses will start with "tn".

Closes #572
This commit is contained in:
zkbot
2016-06-16 18:52:30 +00:00
7 changed files with 151 additions and 45 deletions

View File

@@ -7,6 +7,9 @@
#include "hash.h" #include "hash.h"
#include "uint256.h" #include "uint256.h"
#include "version.h"
#include "streams.h"
#include <assert.h> #include <assert.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
@@ -309,3 +312,72 @@ bool CBitcoinSecret::SetString(const std::string& strSecret)
{ {
return SetString(strSecret.c_str()); return SetString(strSecret.c_str());
} }
const size_t serializedPaymentAddressSize = 64;
bool CZCPaymentAddress::Set(const libzcash::PaymentAddress& addr)
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << addr;
std::vector<unsigned char> addrSerialized(ss.begin(), ss.end());
assert(addrSerialized.size() == serializedPaymentAddressSize);
SetData(Params().Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS), &addrSerialized[0], serializedPaymentAddressSize);
return true;
}
libzcash::PaymentAddress CZCPaymentAddress::Get() const
{
if (vchData.size() != serializedPaymentAddressSize) {
throw std::runtime_error(
"payment address is invalid"
);
}
if (vchVersion != Params().Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS)) {
throw std::runtime_error(
"payment address is for wrong network type"
);
}
std::vector<unsigned char> serialized(vchData.begin(), vchData.end());
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
libzcash::PaymentAddress ret;
ss >> ret;
return ret;
}
const size_t serializedSpendingKeySize = 32;
bool CZCSpendingKey::Set(const libzcash::SpendingKey& addr)
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << addr;
std::vector<unsigned char> addrSerialized(ss.begin(), ss.end());
assert(addrSerialized.size() == serializedSpendingKeySize);
SetData(Params().Base58Prefix(CChainParams::ZCSPENDING_KEY), &addrSerialized[0], serializedSpendingKeySize);
return true;
}
libzcash::SpendingKey CZCSpendingKey::Get() const
{
if (vchData.size() != serializedSpendingKeySize) {
throw std::runtime_error(
"spending key is invalid"
);
}
if (vchVersion != Params().Base58Prefix(CChainParams::ZCSPENDING_KEY)) {
throw std::runtime_error(
"spending key is for wrong network type"
);
}
std::vector<unsigned char> serialized(vchData.begin(), vchData.end());
CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION);
libzcash::SpendingKey ret;
ss >> ret;
return ret;
}

View File

@@ -20,6 +20,7 @@
#include "script/script.h" #include "script/script.h"
#include "script/standard.h" #include "script/standard.h"
#include "support/allocators/zeroafterfree.h" #include "support/allocators/zeroafterfree.h"
#include "zcash/Address.hpp"
#include <string> #include <string>
#include <vector> #include <vector>
@@ -95,6 +96,28 @@ public:
bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; } bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; }
}; };
class CZCPaymentAddress : public CBase58Data {
public:
bool Set(const libzcash::PaymentAddress& addr);
CZCPaymentAddress() {}
CZCPaymentAddress(const std::string& strAddress) { SetString(strAddress.c_str(), 2); }
CZCPaymentAddress(const libzcash::PaymentAddress& addr) { Set(addr); }
libzcash::PaymentAddress Get() const;
};
class CZCSpendingKey : public CBase58Data {
public:
bool Set(const libzcash::SpendingKey& addr);
CZCSpendingKey() {}
CZCSpendingKey(const std::string& strAddress) { SetString(strAddress.c_str(), 2); }
CZCSpendingKey(const libzcash::SpendingKey& addr) { Set(addr); }
libzcash::SpendingKey Get() const;
};
/** base58-encoded Bitcoin addresses. /** base58-encoded Bitcoin addresses.
* Public-key-hash-addresses have version 0 (or 111 testnet). * Public-key-hash-addresses have version 0 (or 111 testnet).
* The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.

View File

@@ -107,6 +107,10 @@ public:
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,128); base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,128);
base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container<std::vector<unsigned char> >(); base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container<std::vector<unsigned char> >();
base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container<std::vector<unsigned char> >(); base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container<std::vector<unsigned char> >();
// guarantees the first two characters, when base58 encoded, are "zc"
base58Prefixes[ZCPAYMENT_ADDRRESS] = {22,154};
// guarantees the first two characters, when base58 encoded, are "SK"
base58Prefixes[ZCSPENDING_KEY] = {171,54};
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
@@ -184,6 +188,8 @@ public:
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239); base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);
base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container<std::vector<unsigned char> >(); base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container<std::vector<unsigned char> >();
base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container<std::vector<unsigned char> >(); base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container<std::vector<unsigned char> >();
base58Prefixes[ZCPAYMENT_ADDRRESS] = {20,81};
base58Prefixes[ZCSPENDING_KEY] = {177,235};
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));

View File

@@ -42,6 +42,9 @@ public:
EXT_PUBLIC_KEY, EXT_PUBLIC_KEY,
EXT_SECRET_KEY, EXT_SECRET_KEY,
ZCPAYMENT_ADDRRESS,
ZCSPENDING_KEY,
MAX_BASE58_TYPES MAX_BASE58_TYPES
}; };

View File

@@ -11,12 +11,15 @@
#include "utilstrencodings.h" #include "utilstrencodings.h"
#include "test/test_bitcoin.h" #include "test/test_bitcoin.h"
#include "zcash/Address.hpp"
#include <string> #include <string>
#include <vector> #include <vector>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
using namespace std; using namespace std;
using namespace libzcash;
static const string strSecret1 ("5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"); static const string strSecret1 ("5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj");
static const string strSecret2 ("5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"); static const string strSecret2 ("5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3");
@@ -188,4 +191,37 @@ BOOST_AUTO_TEST_CASE(key_test1)
BOOST_CHECK(detsigc == ParseHex("2052d8a32079c11e79db95af63bb9600c5b04f21a9ca33dc129c2bfa8ac9dc1cd561d8ae5e0f6c1a16bde3719c64c2fd70e404b6428ab9a69566962e8771b5944d")); BOOST_CHECK(detsigc == ParseHex("2052d8a32079c11e79db95af63bb9600c5b04f21a9ca33dc129c2bfa8ac9dc1cd561d8ae5e0f6c1a16bde3719c64c2fd70e404b6428ab9a69566962e8771b5944d"));
} }
BOOST_AUTO_TEST_CASE(zc_address_test)
{
for (size_t i = 0; i < 1000; i++) {
auto sk = SpendingKey::random();
{
CZCSpendingKey spendingkey(sk);
string sk_string = spendingkey.ToString();
BOOST_CHECK(sk_string[0] == 'S');
BOOST_CHECK(sk_string[1] == 'K');
CZCSpendingKey spendingkey2(sk_string);
SpendingKey sk2 = spendingkey2.Get();
BOOST_CHECK(sk.inner() == sk2.inner());
}
{
auto addr = sk.address();
CZCPaymentAddress paymentaddr(addr);
string addr_string = paymentaddr.ToString();
BOOST_CHECK(addr_string[0] == 'z');
BOOST_CHECK(addr_string[1] == 'c');
CZCPaymentAddress paymentaddr2(addr_string);
PaymentAddress addr2 = paymentaddr2.Get();
BOOST_CHECK(addr.a_pk == addr2.a_pk);
BOOST_CHECK(addr.pk_enc == addr2.pk_enc);
}
}
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View File

@@ -2459,18 +2459,8 @@ Value zc_raw_receive(const json_spirit::Array& params, bool fHelp)
LOCK(cs_main); LOCK(cs_main);
SpendingKey k; CZCSpendingKey spendingkey(params[0].get_str());
SpendingKey k = spendingkey.Get();
{
CDataStream ssData(ParseHexV(params[0], "zcsecretkey"), SER_NETWORK, PROTOCOL_VERSION);
try {
ssData >> k;
} catch(const std::exception &) {
throw runtime_error(
"zcsecretkey could not be decoded"
);
}
}
uint256 epk; uint256 epk;
unsigned char nonce; unsigned char nonce;
@@ -2581,18 +2571,8 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
BOOST_FOREACH(const Pair& s, inputs) BOOST_FOREACH(const Pair& s, inputs)
{ {
SpendingKey k; CZCSpendingKey spendingkey(s.value_.get_str());
SpendingKey k = spendingkey.Get();
{
CDataStream ssData(ParseHexV(s.value_, "zcsecretkey"), SER_NETWORK, PROTOCOL_VERSION);
try {
ssData >> k;
} catch(const std::exception &) {
throw runtime_error(
"zcsecretkey could not be decoded"
);
}
}
keys.push_back(k); keys.push_back(k);
@@ -2634,11 +2614,8 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
BOOST_FOREACH(const Pair& s, outputs) BOOST_FOREACH(const Pair& s, outputs)
{ {
PaymentAddress addrTo; CZCPaymentAddress pubaddr(s.name_);
{ PaymentAddress addrTo = pubaddr.Get();
CDataStream ssData(ParseHexV(s.name_, "to_address"), SER_NETWORK, PROTOCOL_VERSION);
ssData >> addrTo;
}
CAmount nAmount = AmountFromValue(s.value_); CAmount nAmount = AmountFromValue(s.value_);
vpourout.push_back(JSOutput(addrTo, nAmount)); vpourout.push_back(JSOutput(addrTo, nAmount));
@@ -2751,21 +2728,17 @@ Value zc_raw_keygen(const json_spirit::Array& params, bool fHelp)
auto addr = k.address(); auto addr = k.address();
auto viewing_key = k.viewing_key(); auto viewing_key = k.viewing_key();
CDataStream pub(SER_NETWORK, PROTOCOL_VERSION);
CDataStream priv(SER_NETWORK, PROTOCOL_VERSION);
CDataStream viewing(SER_NETWORK, PROTOCOL_VERSION); CDataStream viewing(SER_NETWORK, PROTOCOL_VERSION);
pub << addr;
priv << k;
viewing << viewing_key; viewing << viewing_key;
std::string pub_hex = HexStr(pub.begin(), pub.end()); CZCPaymentAddress pubaddr(addr);
std::string priv_hex = HexStr(priv.begin(), priv.end()); CZCSpendingKey spendingkey(k);
std::string viewing_hex = HexStr(viewing.begin(), viewing.end()); std::string viewing_hex = HexStr(viewing.begin(), viewing.end());
Object result; Object result;
result.push_back(Pair("zcaddress", pub_hex)); result.push_back(Pair("zcaddress", pubaddr.ToString()));
result.push_back(Pair("zcsecretkey", priv_hex)); result.push_back(Pair("zcsecretkey", spendingkey.ToString()));
result.push_back(Pair("zcviewingkey", viewing_hex)); result.push_back(Pair("zcviewingkey", viewing_hex));
return result; return result;
} }

View File

@@ -19,13 +19,6 @@ public:
template <typename Stream, typename Operation> template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
unsigned char leadingByte = 0x92;
READWRITE(leadingByte);
if (leadingByte != 0x92) {
throw std::ios_base::failure("unrecognized payment address lead byte");
}
READWRITE(a_pk); READWRITE(a_pk);
READWRITE(pk_enc); READWRITE(pk_enc);
} }