Add unified full viewing keys and Zcash-internal unified addresses.
This commit is contained in:
parent
83dee7e886
commit
f6ad523ccb
|
@ -108,6 +108,16 @@ class blob88 : public base_blob<88> {
|
|||
public:
|
||||
blob88() {}
|
||||
blob88(const base_blob<88>& b) : base_blob<88>(b) {}
|
||||
blob88(uint64_t i): base_blob<88>() {
|
||||
data[0] = (uint8_t) i;
|
||||
data[1] = (uint8_t) (i >> 8);
|
||||
data[2] = (uint8_t) (i >> 16);
|
||||
data[3] = (uint8_t) (i >> 24);
|
||||
data[4] = (uint8_t) (i >> 32);
|
||||
data[5] = (uint8_t) (i >> 40);
|
||||
data[6] = (uint8_t) (i >> 48);
|
||||
data[7] = (uint8_t) (i >> 56);
|
||||
}
|
||||
explicit blob88(const std::vector<unsigned char>& vch) : base_blob<88>(vch) {}
|
||||
|
||||
std::optional<blob88> increment() const {
|
||||
|
@ -123,6 +133,17 @@ public:
|
|||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// treat as little-endian for numeric comparison
|
||||
bool less_than_le(const blob88& other) const {
|
||||
for (int i = 10; i >= 0; i--) {
|
||||
if (data[i] < other.data[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 160-bit opaque blob.
|
||||
|
|
|
@ -75,6 +75,15 @@ static UniValue ValueFromString(const std::string &str)
|
|||
return value;
|
||||
}
|
||||
|
||||
static SaplingPaymentAddress DefaultSaplingAddress(CWallet* pwallet) {
|
||||
auto usk = pwallet->GetUnifiedSpendingKeyForAccount(0);
|
||||
|
||||
return usk.value()
|
||||
.ToFullViewingKey()
|
||||
.GetSaplingExtendedFullViewingKey().value()
|
||||
.FindAddress(libzcash::diversifier_index_t(0)).first;
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(rpc_wallet_tests, WalletTestingSetup)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rpc_addmultisig)
|
||||
|
@ -788,12 +797,15 @@ void CheckHaveAddr(const libzcash::PaymentAddress& addr) {
|
|||
|
||||
BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) {
|
||||
using namespace libzcash;
|
||||
UniValue addr;
|
||||
|
||||
if (!pwalletMain->HaveMnemonicSeed()) {
|
||||
pwalletMain->GenerateNewSeed();
|
||||
if (!pwalletMain->HaveLegacyHDSeed()) {
|
||||
// fake a legacy seed by creating a separate mnemonic seed
|
||||
auto seed = MnemonicSeed::Random(1);
|
||||
pwalletMain->LoadLegacyHDSeed(seed);
|
||||
}
|
||||
|
||||
UniValue addr;
|
||||
|
||||
KeyIO keyIO(Params());
|
||||
// No parameter defaults to sapling address
|
||||
addr = CallRPC("z_getnewaddress");
|
||||
|
@ -1459,7 +1471,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling)
|
|||
// add keys manually
|
||||
auto taddr = pwalletMain->GenerateNewKey().GetID();
|
||||
std::string taddr1 = keyIO.EncodeDestination(taddr);
|
||||
auto pa = pwalletMain->GenerateNewLegacySaplingZKey().value();
|
||||
auto pa = DefaultSaplingAddress(pwalletMain);
|
||||
std::string zaddr1 = keyIO.EncodePaymentAddress(pa);
|
||||
|
||||
const Consensus::Params& consensusParams = Params().GetConsensus();
|
||||
|
@ -1549,6 +1561,13 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling)
|
|||
BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_zkeys)
|
||||
{
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
if (!pwalletMain->HaveLegacyHDSeed()) {
|
||||
// fake a legacy seed by creating a separate mnemonic seed
|
||||
auto seed = MnemonicSeed::Random(1);
|
||||
pwalletMain->LoadLegacyHDSeed(seed);
|
||||
}
|
||||
|
||||
UniValue retValue;
|
||||
int n = 100;
|
||||
|
||||
|
@ -1605,6 +1624,13 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_zkeys)
|
|||
BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_sapzkeys)
|
||||
{
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
if (!pwalletMain->HaveLegacyHDSeed()) {
|
||||
// fake a legacy seed by creating a separate mnemonic seed
|
||||
auto seed = MnemonicSeed::Random(1);
|
||||
pwalletMain->LoadLegacyHDSeed(seed);
|
||||
}
|
||||
|
||||
UniValue retValue;
|
||||
int n = 100;
|
||||
|
||||
|
@ -1668,6 +1694,12 @@ BOOST_AUTO_TEST_CASE(rpc_z_listunspent_parameters)
|
|||
{
|
||||
SelectParams(CBaseChainParams::TESTNET);
|
||||
|
||||
if (!pwalletMain->HaveLegacyHDSeed()) {
|
||||
// fake a legacy seed by creating a separate mnemonic seed
|
||||
auto seed = MnemonicSeed::Random(1);
|
||||
pwalletMain->LoadLegacyHDSeed(seed);
|
||||
}
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
UniValue retValue;
|
||||
|
|
|
@ -19,6 +19,8 @@ const unsigned char ZCASH_HD_SEED_FP_PERSONAL[BLAKE2bPersonalBytes] =
|
|||
const unsigned char ZCASH_TADDR_OVK_PERSONAL[BLAKE2bPersonalBytes] =
|
||||
{'Z', 'c', 'T', 'a', 'd', 'd', 'r', 'T', 'o', 'S', 'a', 'p', 'l', 'i', 'n', 'g'};
|
||||
|
||||
const libzcash::diversifier_index_t MAX_TRANSPARENT_CHILD_IDX(0x40000000);
|
||||
|
||||
MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, size_t entropyLen)
|
||||
{
|
||||
assert(entropyLen >= 32);
|
||||
|
@ -216,10 +218,11 @@ std::optional<CExtKey> DeriveZip32TransparentSpendingKey(const HDSeed& seed, uin
|
|||
|
||||
std::optional<std::pair<UnifiedSpendingKey, CKeyMetadata>> UnifiedSpendingKey::Derive(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId) {
|
||||
UnifiedSpendingKey usk;
|
||||
usk.accountId = accountId;
|
||||
|
||||
auto transparentKey = DeriveZip32TransparentSpendingKey(seed, bip44CoinType, accountId);
|
||||
if (!transparentKey.has_value()) return std::nullopt;
|
||||
usk.p2pkhKey = transparentKey.value();
|
||||
usk.transparentKey = transparentKey.value();
|
||||
|
||||
auto saplingKey = SaplingExtendedSpendingKey::ForAccount(seed, bip44CoinType, accountId);
|
||||
usk.saplingKey = saplingKey.first;
|
||||
|
@ -227,6 +230,51 @@ std::optional<std::pair<UnifiedSpendingKey, CKeyMetadata>> UnifiedSpendingKey::D
|
|||
return std::make_pair(usk, saplingKey.second);
|
||||
}
|
||||
|
||||
UnifiedFullViewingKey UnifiedSpendingKey::ToFullViewingKey() const {
|
||||
UnifiedFullViewingKey ufvk;
|
||||
|
||||
if (transparentKey.has_value()) {
|
||||
ufvk.transparentKey = transparentKey.value().Neuter();
|
||||
}
|
||||
|
||||
if (saplingKey.has_value()) {
|
||||
ufvk.saplingKey = saplingKey.value().ToXFVK();
|
||||
}
|
||||
|
||||
return ufvk;
|
||||
}
|
||||
|
||||
std::optional<ZcashdUnifiedAddress> UnifiedFullViewingKey::Address(diversifier_index_t j) const {
|
||||
ZcashdUnifiedAddress ua;
|
||||
|
||||
if (transparentKey.has_value()) {
|
||||
if (MAX_TRANSPARENT_CHILD_IDX.less_than_le(j)) return std::nullopt;
|
||||
CExtPubKey changeKey;
|
||||
if (!transparentKey.value().Derive(changeKey, 0)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
CExtPubKey childKey;
|
||||
unsigned int childIndex = (unsigned int) j.GetUint64(0);
|
||||
if (changeKey.Derive(childKey, childIndex)) {
|
||||
ua.transparentKey = childKey.pubkey;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
if (saplingKey.has_value()) {
|
||||
auto saplingAddress = saplingKey.value().Address(j);
|
||||
if (saplingAddress.has_value()) {
|
||||
ua.saplingAddress = saplingAddress.value();
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
return ua;
|
||||
}
|
||||
|
||||
std::optional<unsigned long> ParseZip32KeypathAccount(const std::string& keyPath) {
|
||||
std::regex pattern("m/32'/[0-9]+'/([0-9]+)'");
|
||||
std::smatch matches;
|
||||
|
|
|
@ -281,19 +281,67 @@ struct SaplingExtendedSpendingKey {
|
|||
}
|
||||
};
|
||||
|
||||
class UnifiedSpendingKey;
|
||||
class UnifiedFullViewingKey;
|
||||
|
||||
class ZcashdUnifiedAddress {
|
||||
private:
|
||||
diversifier_index_t diversifier_index;
|
||||
std::optional<CPubKey> transparentKey; //TODO: should this just be the public key hash?
|
||||
std::optional<SaplingPaymentAddress> saplingAddress;
|
||||
|
||||
friend class UnifiedFullViewingKey;
|
||||
|
||||
ZcashdUnifiedAddress() {}
|
||||
public:
|
||||
const std::optional<CPubKey>& GetTransparentKey() const {
|
||||
return transparentKey;
|
||||
}
|
||||
|
||||
const std::optional<SaplingPaymentAddress>& GetSaplingPaymentAddress() const {
|
||||
return saplingAddress;
|
||||
}
|
||||
};
|
||||
|
||||
class UnifiedFullViewingKey {
|
||||
private:
|
||||
std::optional<CExtPubKey> transparentKey;
|
||||
std::optional<SaplingExtendedFullViewingKey> saplingKey;
|
||||
|
||||
UnifiedFullViewingKey() {}
|
||||
|
||||
friend class UnifiedSpendingKey;
|
||||
public:
|
||||
const std::optional<CExtPubKey>& GetTransparentKey() const {
|
||||
return transparentKey;
|
||||
}
|
||||
|
||||
const std::optional<SaplingExtendedFullViewingKey>& GetSaplingExtendedFullViewingKey() const {
|
||||
return saplingKey;
|
||||
}
|
||||
|
||||
std::optional<ZcashdUnifiedAddress> Address(diversifier_index_t j) const;
|
||||
};
|
||||
|
||||
class UnifiedSpendingKey {
|
||||
private:
|
||||
uint32_t accountId;
|
||||
std::optional<CExtKey> p2pkhKey;
|
||||
std::optional<CExtKey> transparentKey;
|
||||
std::optional<SaplingExtendedSpendingKey> saplingKey;
|
||||
|
||||
UnifiedSpendingKey() {}
|
||||
public:
|
||||
static std::optional<std::pair<UnifiedSpendingKey, CKeyMetadata>> Derive(const HDSeed& seed, uint32_t bip44CoinType, uint32_t accountId);
|
||||
|
||||
const std::optional<SaplingExtendedSpendingKey>& GetSaplingExtendedSpendingKey() {
|
||||
const std::optional<CExtKey>& GetTransparentKey() const {
|
||||
return transparentKey;
|
||||
}
|
||||
|
||||
const std::optional<SaplingExtendedSpendingKey>& GetSaplingExtendedSpendingKey() const {
|
||||
return saplingKey;
|
||||
}
|
||||
|
||||
UnifiedFullViewingKey ToFullViewingKey() const;
|
||||
};
|
||||
|
||||
std::optional<unsigned long> ParseZip32KeypathAccount(const std::string& keyPath);
|
||||
|
|
Loading…
Reference in New Issue