Browse Source

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
pull/145/head
zkbot 8 years ago
parent
commit
62a2d12672
  1. 72
      src/base58.cpp
  2. 23
      src/base58.h
  3. 6
      src/chainparams.cpp
  4. 3
      src/chainparams.h
  5. 36
      src/test/key_tests.cpp
  6. 47
      src/wallet/rpcwallet.cpp
  7. 9
      src/zcash/Address.hpp

72
src/base58.cpp

@ -7,6 +7,9 @@
#include "hash.h"
#include "uint256.h"
#include "version.h"
#include "streams.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
@ -309,3 +312,72 @@ bool CBitcoinSecret::SetString(const std::string& strSecret)
{
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;
}

23
src/base58.h

@ -20,6 +20,7 @@
#include "script/script.h"
#include "script/standard.h"
#include "support/allocators/zeroafterfree.h"
#include "zcash/Address.hpp"
#include <string>
#include <vector>
@ -95,6 +96,28 @@ public:
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.
* Public-key-hash-addresses have version 0 (or 111 testnet).
* The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.

6
src/chainparams.cpp

@ -107,6 +107,10 @@ public:
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_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));
@ -184,6 +188,8 @@ public:
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_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));

3
src/chainparams.h

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

36
src/test/key_tests.cpp

@ -11,12 +11,15 @@
#include "utilstrencodings.h"
#include "test/test_bitcoin.h"
#include "zcash/Address.hpp"
#include <string>
#include <vector>
#include <boost/test/unit_test.hpp>
using namespace std;
using namespace libzcash;
static const string strSecret1 ("5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj");
static const string strSecret2 ("5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3");
@ -188,4 +191,37 @@ BOOST_AUTO_TEST_CASE(key_test1)
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()

47
src/wallet/rpcwallet.cpp

@ -2459,18 +2459,8 @@ Value zc_raw_receive(const json_spirit::Array& params, bool fHelp)
LOCK(cs_main);
SpendingKey k;
{
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"
);
}
}
CZCSpendingKey spendingkey(params[0].get_str());
SpendingKey k = spendingkey.Get();
uint256 epk;
unsigned char nonce;
@ -2581,18 +2571,8 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
BOOST_FOREACH(const Pair& s, inputs)
{
SpendingKey k;
{
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"
);
}
}
CZCSpendingKey spendingkey(s.value_.get_str());
SpendingKey k = spendingkey.Get();
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)
{
PaymentAddress addrTo;
{
CDataStream ssData(ParseHexV(s.name_, "to_address"), SER_NETWORK, PROTOCOL_VERSION);
ssData >> addrTo;
}
CZCPaymentAddress pubaddr(s.name_);
PaymentAddress addrTo = pubaddr.Get();
CAmount nAmount = AmountFromValue(s.value_);
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 viewing_key = k.viewing_key();
CDataStream pub(SER_NETWORK, PROTOCOL_VERSION);
CDataStream priv(SER_NETWORK, PROTOCOL_VERSION);
CDataStream viewing(SER_NETWORK, PROTOCOL_VERSION);
pub << addr;
priv << k;
viewing << viewing_key;
std::string pub_hex = HexStr(pub.begin(), pub.end());
std::string priv_hex = HexStr(priv.begin(), priv.end());
CZCPaymentAddress pubaddr(addr);
CZCSpendingKey spendingkey(k);
std::string viewing_hex = HexStr(viewing.begin(), viewing.end());
Object result;
result.push_back(Pair("zcaddress", pub_hex));
result.push_back(Pair("zcsecretkey", priv_hex));
result.push_back(Pair("zcaddress", pubaddr.ToString()));
result.push_back(Pair("zcsecretkey", spendingkey.ToString()));
result.push_back(Pair("zcviewingkey", viewing_hex));
return result;
}

9
src/zcash/Address.hpp

@ -19,13 +19,6 @@ public:
template <typename Stream, typename Operation>
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(pk_enc);
}
@ -51,4 +44,4 @@ public:
}
#endif // _ZCADDRESS_H_
#endif // _ZCADDRESS_H_

Loading…
Cancel
Save