diff --git a/src/base58.cpp b/src/base58.cpp index c80918505..efce9186f 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -7,6 +7,9 @@ #include "hash.h" #include "uint256.h" +#include "version.h" +#include "streams.h" + #include #include #include @@ -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 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 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 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 serialized(vchData.begin(), vchData.end()); + + CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION); + libzcash::SpendingKey ret; + ss >> ret; + return ret; +} + diff --git a/src/base58.h b/src/base58.h index 787979c82..8a442b1bc 100644 --- a/src/base58.h +++ b/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 #include @@ -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. diff --git a/src/chainparams.cpp b/src/chainparams.cpp index b86e0866c..c1cc592e3 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -107,6 +107,10 @@ public: base58Prefixes[SECRET_KEY] = std::vector(1,128); base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container >(); base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container >(); + // 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(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); @@ -184,6 +188,8 @@ public: base58Prefixes[SECRET_KEY] = std::vector(1,239); base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container >(); base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container >(); + base58Prefixes[ZCPAYMENT_ADDRRESS] = {20,81}; + base58Prefixes[ZCSPENDING_KEY] = {177,235}; vFixedSeeds = std::vector(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); diff --git a/src/chainparams.h b/src/chainparams.h index 7169c3241..de89fa0ef 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -42,6 +42,9 @@ public: EXT_PUBLIC_KEY, EXT_SECRET_KEY, + ZCPAYMENT_ADDRRESS, + ZCSPENDING_KEY, + MAX_BASE58_TYPES }; diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 13ca94946..329883b29 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -11,12 +11,15 @@ #include "utilstrencodings.h" #include "test/test_bitcoin.h" +#include "zcash/Address.hpp" + #include #include #include 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() diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 0c7db2570..ae3354c2c 100644 --- a/src/wallet/rpcwallet.cpp +++ b/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; } diff --git a/src/zcash/Address.hpp b/src/zcash/Address.hpp index 91e35bd03..86e351cf7 100644 --- a/src/zcash/Address.hpp +++ b/src/zcash/Address.hpp @@ -19,13 +19,6 @@ public: template 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_ \ No newline at end of file +#endif // _ZCADDRESS_H_