Merge pull request #5595 from str4d/feature/wallet_orchard-ua_updates
Merge current master into Orchard wallet feature branch
This commit is contained in:
commit
41ed311afa
|
@ -18,6 +18,10 @@ $(package)_download_file_aarch64_linux=clang+llvm-$($(package)_version)-aarch64-
|
|||
$(package)_file_name_aarch64_linux=clang-llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz
|
||||
$(package)_sha256_hash_aarch64_linux=968d65d2593850ee9b37fcda074fb7641529bd45d2f976af6c8197de3c22612f
|
||||
|
||||
ifneq (,$(wildcard /etc/arch-release))
|
||||
$(package)_dependencies=native_libtinfo
|
||||
endif
|
||||
|
||||
# Ensure we have clang native to the builder, not the target host
|
||||
ifneq ($(canonical_host),$(build))
|
||||
$(package)_exact_download_path=$($(package)_download_path_$(build_os))
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package=native_tinfo
|
||||
$(package)_version=5.6.0
|
||||
$(package)_download_path_linux=http://ftp.debian.org/debian/pool/main/n/ncurses/
|
||||
$(package)_download_file_linux=libtinfo5_6.0+20161126-1+deb9u2_amd64.deb
|
||||
$(package)_file_name_linux=libtinfo5_6.0+20161126-1+deb9u2_amd64.deb
|
||||
$(package)_sha256_hash_linux=1d249a3193568b5ef785ad8993b9ba6d6fdca0eb359204c2355532b82d25e9f5
|
||||
|
||||
define $(package)_extract_cmds
|
||||
mkdir -p $($(package)_extract_dir) && \
|
||||
echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \
|
||||
$(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \
|
||||
mkdir -p libtinfo5 && \
|
||||
ar x --output libtinfo5 $($(package)_source_dir)/$($(package)_file_name) && \
|
||||
cd libtinfo5 && \
|
||||
tar xf data.tar.xz
|
||||
endef
|
||||
|
||||
define $(package)_stage_cmds
|
||||
pwd && \
|
||||
mkdir -p $($(package)_staging_prefix_dir)/lib && \
|
||||
cp libtinfo5/lib/x86_64-linux-gnu/libtinfo.so.5.9 $($(package)_staging_prefix_dir)/lib/libtinfo.so.5
|
||||
endef
|
|
@ -1,6 +1,10 @@
|
|||
zcash_packages := libsodium utfcpp
|
||||
packages := boost libevent zeromq $(zcash_packages) googletest
|
||||
native_packages := native_clang native_ccache native_rust
|
||||
native_packages := native_clang native_ccache native_rust
|
||||
|
||||
ifneq (,$(wildcard /etc/arch-release))
|
||||
native_packages += native_libtinfo
|
||||
endif
|
||||
|
||||
wallet_packages=bdb
|
||||
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
NU5_BRANCH_ID,
|
||||
assert_equal,
|
||||
connect_nodes_bi,
|
||||
get_coinbase_address,
|
||||
nuparams,
|
||||
start_nodes,
|
||||
wait_and_assert_operationid_status,
|
||||
)
|
||||
|
@ -17,6 +19,7 @@ from decimal import Decimal
|
|||
|
||||
SPROUT_TREE_EMPTY_ROOT = "59d2cde5e65c1414c32ba54f0fe4bdb3d67618125286e6a191317917c812c6d7"
|
||||
SAPLING_TREE_EMPTY_ROOT = "3e49b5f954aa9d3545bc6c37744661eea48d7c34e3000d82b7f0010c30f4c2fb"
|
||||
ORCHARD_TREE_EMPTY_ROOT = "2fd8e51a03d9bbe2dd809831b1497aeb68a6e37ddf707ced4aa2d8dff13529ae"
|
||||
NULL_FIELD = "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
|
||||
# Verify block header field 'hashFinalSaplingRoot' (returned in rpc as 'finalsaplingroot')
|
||||
|
@ -25,17 +28,16 @@ class FinalSaplingRootTest(BitcoinTestFramework):
|
|||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 4
|
||||
self.num_nodes = 2
|
||||
self.setup_clean_chain = True
|
||||
|
||||
def setup_network(self, split=False):
|
||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[
|
||||
'-txindex' # Avoid JSONRPC error: No information available about transaction
|
||||
'-txindex', # Avoid JSONRPC error: No information available about transaction
|
||||
nuparams(NU5_BRANCH_ID, 210),
|
||||
'-debug',
|
||||
]] * self.num_nodes)
|
||||
connect_nodes_bi(self.nodes,0,1)
|
||||
connect_nodes_bi(self.nodes,1,2)
|
||||
connect_nodes_bi(self.nodes,0,2)
|
||||
connect_nodes_bi(self.nodes,0,3)
|
||||
self.is_network_split=False
|
||||
self.sync_all()
|
||||
|
||||
|
@ -52,13 +54,19 @@ class FinalSaplingRootTest(BitcoinTestFramework):
|
|||
|
||||
assert_equal(treestate["sprout"]["commitments"]["finalRoot"], SPROUT_TREE_EMPTY_ROOT)
|
||||
assert_equal(treestate["sprout"]["commitments"]["finalState"], "000000")
|
||||
assert("skipHash" not in treestate["sprout"])
|
||||
assert "skipHash" not in treestate["sprout"]
|
||||
|
||||
assert_equal(treestate["sapling"]["commitments"]["finalRoot"], NULL_FIELD)
|
||||
# There is no sapling state tree yet, and trying to find it in an earlier
|
||||
# block won't succeed (we're at genesis block), so skipHash is absent.
|
||||
assert("finalState" not in treestate["sapling"])
|
||||
assert("skipHash" not in treestate["sapling"])
|
||||
assert "finalState" not in treestate["sapling"]
|
||||
assert "skipHash" not in treestate["sapling"]
|
||||
|
||||
assert_equal(treestate["orchard"]["commitments"]["finalRoot"], NULL_FIELD)
|
||||
# There is no orchard state tree yet, and trying to find it in an earlier
|
||||
# block won't succeed (we're at genesis block), so skipHash is absent.
|
||||
assert "finalState" not in treestate["orchard"]
|
||||
assert "skipHash" not in treestate["orchard"]
|
||||
|
||||
# Verify all generated blocks contain the empty root of the Sapling tree.
|
||||
blockcount = self.nodes[0].getblockcount()
|
||||
|
@ -70,14 +78,18 @@ class FinalSaplingRootTest(BitcoinTestFramework):
|
|||
assert_equal(treestate["height"], height)
|
||||
assert_equal(treestate["hash"], self.nodes[0].getblockhash(height))
|
||||
|
||||
assert("skipHash" not in treestate["sprout"])
|
||||
assert "skipHash" not in treestate["sprout"]
|
||||
assert_equal(treestate["sprout"]["commitments"]["finalRoot"], SPROUT_TREE_EMPTY_ROOT)
|
||||
assert_equal(treestate["sprout"]["commitments"]["finalState"], "000000")
|
||||
|
||||
assert("skipHash" not in treestate["sapling"])
|
||||
|
||||
assert "skipHash" not in treestate["sapling"]
|
||||
assert_equal(treestate["sapling"]["commitments"]["finalRoot"], SAPLING_TREE_EMPTY_ROOT)
|
||||
assert_equal(treestate["sapling"]["commitments"]["finalState"], "000000")
|
||||
|
||||
assert "skipHash" not in treestate["orchard"]
|
||||
assert_equal(treestate["orchard"]["commitments"]["finalRoot"], NULL_FIELD)
|
||||
|
||||
# Node 0 shields some funds
|
||||
taddr0 = get_coinbase_address(self.nodes[0])
|
||||
saplingAddr0 = self.nodes[0].z_getnewaddress('sapling')
|
||||
|
@ -93,8 +105,8 @@ class FinalSaplingRootTest(BitcoinTestFramework):
|
|||
# Verify the final Sapling root has changed
|
||||
blk = self.nodes[0].getblock("201")
|
||||
root = blk["finalsaplingroot"]
|
||||
assert(root is not SAPLING_TREE_EMPTY_ROOT)
|
||||
assert(root is not NULL_FIELD)
|
||||
assert root is not SAPLING_TREE_EMPTY_ROOT
|
||||
assert root is not NULL_FIELD
|
||||
|
||||
# Verify there is a Sapling output description (its commitment was added to tree)
|
||||
result = self.nodes[0].getrawtransaction(mytxid, 1)
|
||||
|
@ -105,8 +117,8 @@ class FinalSaplingRootTest(BitcoinTestFramework):
|
|||
new_treestate = self.nodes[0].z_gettreestate(str(-1))
|
||||
assert_equal(new_treestate["sapling"]["commitments"]["finalRoot"], root)
|
||||
assert_equal(new_treestate["sprout"], treestate["sprout"])
|
||||
assert(new_treestate["sapling"]["commitments"]["finalRoot"] != treestate["sapling"]["commitments"]["finalRoot"])
|
||||
assert(new_treestate["sapling"]["commitments"]["finalState"] != treestate["sapling"]["commitments"]["finalState"])
|
||||
assert new_treestate["sapling"]["commitments"]["finalRoot"] != treestate["sapling"]["commitments"]["finalRoot"]
|
||||
assert new_treestate["sapling"]["commitments"]["finalState"] != treestate["sapling"]["commitments"]["finalState"]
|
||||
assert_equal(len(new_treestate["sapling"]["commitments"]["finalRoot"]), 64)
|
||||
assert_equal(len(new_treestate["sapling"]["commitments"]["finalState"]), 70)
|
||||
treestate = new_treestate
|
||||
|
@ -145,8 +157,8 @@ class FinalSaplingRootTest(BitcoinTestFramework):
|
|||
new_treestate = self.nodes[0].z_gettreestate(str(-1))
|
||||
assert_equal(new_treestate["sapling"]["commitments"]["finalRoot"], root)
|
||||
assert_equal(new_treestate["sapling"], treestate["sapling"])
|
||||
assert(new_treestate["sprout"]["commitments"]["finalRoot"] != treestate["sprout"]["commitments"]["finalRoot"])
|
||||
assert(new_treestate["sprout"]["commitments"]["finalState"] != treestate["sprout"]["commitments"]["finalState"])
|
||||
assert new_treestate["sprout"]["commitments"]["finalRoot"] != treestate["sprout"]["commitments"]["finalRoot"]
|
||||
assert new_treestate["sprout"]["commitments"]["finalState"] != treestate["sprout"]["commitments"]["finalState"]
|
||||
assert_equal(len(new_treestate["sprout"]["commitments"]["finalRoot"]), 64)
|
||||
assert_equal(len(new_treestate["sprout"]["commitments"]["finalState"]), 134)
|
||||
treestate = new_treestate
|
||||
|
@ -164,7 +176,7 @@ class FinalSaplingRootTest(BitcoinTestFramework):
|
|||
|
||||
assert_equal(len(self.nodes[0].getblock("205")["tx"]), 2)
|
||||
assert_equal(self.nodes[1].z_getbalance(saplingAddr1), Decimal("12.34"))
|
||||
assert(root is not self.nodes[0].getblock("205")["finalsaplingroot"])
|
||||
assert root is not self.nodes[0].getblock("205")["finalsaplingroot"]
|
||||
|
||||
# Verify there is a Sapling output description (its commitment was added to tree)
|
||||
result = self.nodes[0].getrawtransaction(mytxid, 1)
|
||||
|
@ -172,8 +184,8 @@ class FinalSaplingRootTest(BitcoinTestFramework):
|
|||
|
||||
new_treestate = self.nodes[0].z_gettreestate(str(-1))
|
||||
assert_equal(new_treestate["sprout"], treestate["sprout"])
|
||||
assert(new_treestate["sapling"]["commitments"]["finalRoot"] != treestate["sapling"]["commitments"]["finalRoot"])
|
||||
assert(new_treestate["sapling"]["commitments"]["finalState"] != treestate["sapling"]["commitments"]["finalState"])
|
||||
assert new_treestate["sapling"]["commitments"]["finalRoot"] != treestate["sapling"]["commitments"]["finalRoot"]
|
||||
assert new_treestate["sapling"]["commitments"]["finalState"] != treestate["sapling"]["commitments"]["finalState"]
|
||||
assert_equal(len(new_treestate["sapling"]["commitments"]["finalRoot"]), 64)
|
||||
assert_equal(len(new_treestate["sapling"]["commitments"]["finalState"]), 136)
|
||||
treestate = new_treestate
|
||||
|
@ -200,6 +212,21 @@ class FinalSaplingRootTest(BitcoinTestFramework):
|
|||
assert_equal(new_treestate["sprout"], treestate["sprout"])
|
||||
assert_equal(new_treestate["sapling"], treestate["sapling"])
|
||||
|
||||
# Activate NU5; more testing should be added once we can mine orchard transactions.
|
||||
self.sync_all()
|
||||
self.nodes[0].generate(4)
|
||||
self.sync_all()
|
||||
new_treestate = self.nodes[0].z_gettreestate(str(-1))
|
||||
|
||||
# sprout and sapling results should not change
|
||||
assert_equal(new_treestate["sprout"], treestate["sprout"])
|
||||
assert_equal(new_treestate["sapling"], treestate["sapling"])
|
||||
|
||||
assert "skipHash" not in treestate["orchard"]
|
||||
assert_equal(new_treestate["orchard"]["commitments"]["finalRoot"], ORCHARD_TREE_EMPTY_ROOT)
|
||||
assert_equal(new_treestate["orchard"]["commitments"]["finalState"], "00")
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
FinalSaplingRootTest().main()
|
||||
|
|
|
@ -385,6 +385,15 @@ class ListReceivedTest (BitcoinTestFramework):
|
|||
assert 'transparent' in receivers
|
||||
assert 'sapling' in receivers
|
||||
assert 'orchard' in receivers
|
||||
assert_raises_message(
|
||||
JSONRPCException,
|
||||
"The provided address is a bare receiver from a Unified Address in this wallet.",
|
||||
node.z_listreceivedbyaddress, receivers['transparent'], 0)
|
||||
assert_raises_message(
|
||||
JSONRPCException,
|
||||
"The provided address is a bare receiver from a Unified Address in this wallet.",
|
||||
node.z_listreceivedbyaddress, receivers['sapling'], 0)
|
||||
|
||||
# Wallet contains no notes
|
||||
r = node.z_listreceivedbyaddress(unified_addr, 0)
|
||||
assert_equal(len(r), 0, "unified_addr should have received zero notes")
|
||||
|
|
105
src/key_io.cpp
105
src/key_io.cpp
|
@ -255,7 +255,7 @@ const size_t ConvertedSaplingExtendedFullViewingKeySize = (ZIP32_XFVK_SIZE * 8 +
|
|||
const size_t ConvertedSaplingExtendedSpendingKeySize = (ZIP32_XSK_SIZE * 8 + 4) / 5;
|
||||
} // namespace
|
||||
|
||||
CTxDestination KeyIO::DecodeDestination(const std::string& str)
|
||||
CTxDestination KeyIO::DecodeDestination(const std::string& str) const
|
||||
{
|
||||
std::vector<unsigned char> data;
|
||||
uint160 hash;
|
||||
|
@ -279,7 +279,7 @@ CTxDestination KeyIO::DecodeDestination(const std::string& str)
|
|||
return CNoDestination();
|
||||
};
|
||||
|
||||
CKey KeyIO::DecodeSecret(const std::string& str)
|
||||
CKey KeyIO::DecodeSecret(const std::string& str) const
|
||||
{
|
||||
CKey key;
|
||||
std::vector<unsigned char> data;
|
||||
|
@ -295,7 +295,7 @@ CKey KeyIO::DecodeSecret(const std::string& str)
|
|||
return key;
|
||||
}
|
||||
|
||||
std::string KeyIO::EncodeSecret(const CKey& key)
|
||||
std::string KeyIO::EncodeSecret(const CKey& key) const
|
||||
{
|
||||
assert(key.IsValid());
|
||||
std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::SECRET_KEY);
|
||||
|
@ -308,7 +308,7 @@ std::string KeyIO::EncodeSecret(const CKey& key)
|
|||
return ret;
|
||||
}
|
||||
|
||||
CExtPubKey KeyIO::DecodeExtPubKey(const std::string& str)
|
||||
CExtPubKey KeyIO::DecodeExtPubKey(const std::string& str) const
|
||||
{
|
||||
CExtPubKey key;
|
||||
std::vector<unsigned char> data;
|
||||
|
@ -321,7 +321,7 @@ CExtPubKey KeyIO::DecodeExtPubKey(const std::string& str)
|
|||
return key;
|
||||
}
|
||||
|
||||
std::string KeyIO::EncodeExtPubKey(const CExtPubKey& key)
|
||||
std::string KeyIO::EncodeExtPubKey(const CExtPubKey& key) const
|
||||
{
|
||||
std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::EXT_PUBLIC_KEY);
|
||||
size_t size = data.size();
|
||||
|
@ -331,7 +331,7 @@ std::string KeyIO::EncodeExtPubKey(const CExtPubKey& key)
|
|||
return ret;
|
||||
}
|
||||
|
||||
CExtKey KeyIO::DecodeExtKey(const std::string& str)
|
||||
CExtKey KeyIO::DecodeExtKey(const std::string& str) const
|
||||
{
|
||||
CExtKey key;
|
||||
std::vector<unsigned char> data;
|
||||
|
@ -344,7 +344,7 @@ CExtKey KeyIO::DecodeExtKey(const std::string& str)
|
|||
return key;
|
||||
}
|
||||
|
||||
std::string KeyIO::EncodeExtKey(const CExtKey& key)
|
||||
std::string KeyIO::EncodeExtKey(const CExtKey& key) const
|
||||
{
|
||||
std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::EXT_SECRET_KEY);
|
||||
size_t size = data.size();
|
||||
|
@ -355,17 +355,17 @@ std::string KeyIO::EncodeExtKey(const CExtKey& key)
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::string KeyIO::EncodeDestination(const CTxDestination& dest)
|
||||
std::string KeyIO::EncodeDestination(const CTxDestination& dest) const
|
||||
{
|
||||
return std::visit(DestinationEncoder(keyConstants), dest);
|
||||
}
|
||||
|
||||
bool KeyIO::IsValidDestinationString(const std::string& str)
|
||||
bool KeyIO::IsValidDestinationString(const std::string& str) const
|
||||
{
|
||||
return IsValidDestination(DecodeDestination(str));
|
||||
}
|
||||
|
||||
std::string KeyIO::EncodePaymentAddress(const libzcash::PaymentAddress& zaddr)
|
||||
std::string KeyIO::EncodePaymentAddress(const libzcash::PaymentAddress& zaddr) const
|
||||
{
|
||||
return std::visit(PaymentAddressEncoder(keyConstants), zaddr);
|
||||
}
|
||||
|
@ -441,78 +441,12 @@ std::optional<T1> DecodeAny(
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
static bool AddOrchardReceiver(void* ua, OrchardRawAddressPtr* ptr)
|
||||
{
|
||||
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(
|
||||
libzcash::OrchardRawAddress::KeyIoOnlyFromReceiver(ptr));
|
||||
}
|
||||
|
||||
/**
|
||||
* `raw` MUST be 43 bytes.
|
||||
*/
|
||||
static bool AddSaplingReceiver(void* ua, const unsigned char* raw)
|
||||
{
|
||||
CDataStream ss(
|
||||
reinterpret_cast<const char*>(raw),
|
||||
reinterpret_cast<const char*>(raw + 43),
|
||||
SER_NETWORK,
|
||||
PROTOCOL_VERSION);
|
||||
libzcash::SaplingPaymentAddress receiver;
|
||||
ss >> receiver;
|
||||
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
|
||||
}
|
||||
|
||||
/**
|
||||
* `raw` MUST be 20 bytes.
|
||||
*/
|
||||
static bool AddP2SHReceiver(void* ua, const unsigned char* raw)
|
||||
{
|
||||
CDataStream ss(
|
||||
reinterpret_cast<const char*>(raw),
|
||||
reinterpret_cast<const char*>(raw + 20),
|
||||
SER_NETWORK,
|
||||
PROTOCOL_VERSION);
|
||||
CScriptID receiver;
|
||||
ss >> receiver;
|
||||
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
|
||||
}
|
||||
|
||||
/**
|
||||
* `raw` MUST be 20 bytes.
|
||||
*/
|
||||
static bool AddP2PKHReceiver(void* ua, const unsigned char* raw)
|
||||
{
|
||||
CDataStream ss(
|
||||
reinterpret_cast<const char*>(raw),
|
||||
reinterpret_cast<const char*>(raw + 20),
|
||||
SER_NETWORK,
|
||||
PROTOCOL_VERSION);
|
||||
CKeyID receiver;
|
||||
ss >> receiver;
|
||||
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
|
||||
}
|
||||
|
||||
static bool AddUnknownReceiver(void* ua, uint32_t typecode, const unsigned char* data, size_t len)
|
||||
{
|
||||
libzcash::UnknownReceiver receiver(typecode, std::vector(data, data + len));
|
||||
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
|
||||
}
|
||||
|
||||
std::optional<libzcash::PaymentAddress> KeyIO::DecodePaymentAddress(const std::string& str)
|
||||
std::optional<libzcash::PaymentAddress> KeyIO::DecodePaymentAddress(const std::string& str) const
|
||||
{
|
||||
// Try parsing as a Unified Address.
|
||||
libzcash::UnifiedAddress ua;
|
||||
if (zcash_address_parse_unified(
|
||||
str.c_str(),
|
||||
keyConstants.NetworkIDString().c_str(),
|
||||
&ua,
|
||||
AddOrchardReceiver,
|
||||
AddSaplingReceiver,
|
||||
AddP2SHReceiver,
|
||||
AddP2PKHReceiver,
|
||||
AddUnknownReceiver)
|
||||
) {
|
||||
return ua;
|
||||
auto ua = libzcash::UnifiedAddress::Parse(keyConstants, str);
|
||||
if (ua.has_value()) {
|
||||
return ua.value();
|
||||
}
|
||||
|
||||
// Try parsing as a Sapling address
|
||||
|
@ -550,16 +484,17 @@ std::optional<libzcash::PaymentAddress> KeyIO::DecodePaymentAddress(const std::s
|
|||
}, DecodeDestination(str));
|
||||
}
|
||||
|
||||
bool KeyIO::IsValidPaymentAddressString(const std::string& str) {
|
||||
bool KeyIO::IsValidPaymentAddressString(const std::string& str) const
|
||||
{
|
||||
return DecodePaymentAddress(str).has_value();
|
||||
}
|
||||
|
||||
std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk)
|
||||
std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk) const
|
||||
{
|
||||
return std::visit(ViewingKeyEncoder(keyConstants), vk);
|
||||
}
|
||||
|
||||
std::optional<libzcash::ViewingKey> KeyIO::DecodeViewingKey(const std::string& str)
|
||||
std::optional<libzcash::ViewingKey> KeyIO::DecodeViewingKey(const std::string& str) const
|
||||
{
|
||||
// Try parsing as a Unified full viewing key
|
||||
auto ufvk = libzcash::UnifiedFullViewingKey::Decode(str, keyConstants);
|
||||
|
@ -578,12 +513,12 @@ std::optional<libzcash::ViewingKey> KeyIO::DecodeViewingKey(const std::string& s
|
|||
);
|
||||
}
|
||||
|
||||
std::string KeyIO::EncodeSpendingKey(const libzcash::SpendingKey& zkey)
|
||||
std::string KeyIO::EncodeSpendingKey(const libzcash::SpendingKey& zkey) const
|
||||
{
|
||||
return std::visit(SpendingKeyEncoder(keyConstants), zkey);
|
||||
}
|
||||
|
||||
std::optional<libzcash::SpendingKey> KeyIO::DecodeSpendingKey(const std::string& str)
|
||||
std::optional<libzcash::SpendingKey> KeyIO::DecodeSpendingKey(const std::string& str) const
|
||||
{
|
||||
|
||||
return DecodeAny<libzcash::SpendingKey,
|
||||
|
|
32
src/key_io.h
32
src/key_io.h
|
@ -23,28 +23,28 @@ private:
|
|||
public:
|
||||
KeyIO(const KeyConstants& keyConstants): keyConstants(keyConstants) { }
|
||||
|
||||
CKey DecodeSecret(const std::string& str);
|
||||
std::string EncodeSecret(const CKey& key);
|
||||
CKey DecodeSecret(const std::string& str) const;
|
||||
std::string EncodeSecret(const CKey& key) const;
|
||||
|
||||
CExtKey DecodeExtKey(const std::string& str);
|
||||
std::string EncodeExtKey(const CExtKey& extkey);
|
||||
CExtPubKey DecodeExtPubKey(const std::string& str);
|
||||
std::string EncodeExtPubKey(const CExtPubKey& extpubkey);
|
||||
CExtKey DecodeExtKey(const std::string& str) const;
|
||||
std::string EncodeExtKey(const CExtKey& extkey) const;
|
||||
CExtPubKey DecodeExtPubKey(const std::string& str) const;
|
||||
std::string EncodeExtPubKey(const CExtPubKey& extpubkey) const;
|
||||
|
||||
std::string EncodeDestination(const CTxDestination& dest);
|
||||
CTxDestination DecodeDestination(const std::string& str);
|
||||
std::string EncodeDestination(const CTxDestination& dest) const;
|
||||
CTxDestination DecodeDestination(const std::string& str) const;
|
||||
|
||||
bool IsValidDestinationString(const std::string& str);
|
||||
bool IsValidDestinationString(const std::string& str) const;
|
||||
|
||||
std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr);
|
||||
std::optional<libzcash::PaymentAddress> DecodePaymentAddress(const std::string& str);
|
||||
bool IsValidPaymentAddressString(const std::string& str);
|
||||
std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr) const;
|
||||
std::optional<libzcash::PaymentAddress> DecodePaymentAddress(const std::string& str) const;
|
||||
bool IsValidPaymentAddressString(const std::string& str) const;
|
||||
|
||||
std::string EncodeViewingKey(const libzcash::ViewingKey& vk);
|
||||
std::optional<libzcash::ViewingKey> DecodeViewingKey(const std::string& str);
|
||||
std::string EncodeViewingKey(const libzcash::ViewingKey& vk) const;
|
||||
std::optional<libzcash::ViewingKey> DecodeViewingKey(const std::string& str) const;
|
||||
|
||||
std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey);
|
||||
std::optional<libzcash::SpendingKey> DecodeSpendingKey(const std::string& str);
|
||||
std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey) const;
|
||||
std::optional<libzcash::SpendingKey> DecodeSpendingKey(const std::string& str) const;
|
||||
};
|
||||
|
||||
#endif // BITCOIN_KEY_IO_H
|
||||
|
|
|
@ -1247,6 +1247,13 @@ UniValue z_gettreestate(const UniValue& params, bool fHelp)
|
|||
" \"finalRoot\": \"hex\", (string)\n"
|
||||
" \"finalState\": \"hex\" (string)\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"orchard\": {\n"
|
||||
" \"skipHash\": \"hash\", (string) hash of most recent block with more information\n"
|
||||
" \"commitments\": {\n"
|
||||
" \"finalRoot\": \"hex\", (string)\n"
|
||||
" \"finalState\": \"hex\" (string)\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
|
@ -1327,6 +1334,31 @@ UniValue z_gettreestate(const UniValue& params, bool fHelp)
|
|||
res.pushKV("sapling", sapling_result);
|
||||
}
|
||||
|
||||
// orchard
|
||||
{
|
||||
UniValue orchard_result(UniValue::VOBJ);
|
||||
UniValue orchard_commitments(UniValue::VOBJ);
|
||||
orchard_commitments.pushKV("finalRoot", pindex->hashFinalOrchardRoot.GetHex());
|
||||
bool need_skiphash = false;
|
||||
OrchardMerkleFrontier tree;
|
||||
if (pcoinsTip->GetOrchardAnchorAt(pindex->hashFinalOrchardRoot, tree)) {
|
||||
CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
|
||||
s << tree;
|
||||
orchard_commitments.pushKV("finalState", HexStr(s.begin(), s.end()));
|
||||
} else {
|
||||
// Set skipHash to the most recent block that has a finalState.
|
||||
const CBlockIndex* pindex_skip = pindex->pprev;
|
||||
while (pindex_skip && !pcoinsTip->GetOrchardAnchorAt(pindex_skip->hashFinalOrchardRoot, tree)) {
|
||||
pindex_skip = pindex_skip->pprev;
|
||||
}
|
||||
if (pindex_skip) {
|
||||
orchard_result.pushKV("skipHash", pindex_skip->GetBlockHash().GetHex());
|
||||
}
|
||||
}
|
||||
orchard_result.pushKV("commitments", orchard_commitments);
|
||||
res.pushKV("orchard", orchard_result);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,27 +6,6 @@
|
|||
|
||||
extern UniValue signrawtransaction(const UniValue& params, bool fHelp);
|
||||
|
||||
// TODO: instead of passing a TestMode flag, tests should override `CommitTransaction`
|
||||
// on the wallet.
|
||||
UniValue SendTransaction(CTransaction& tx, std::optional<std::reference_wrapper<CReserveKey>> reservekey, bool testmode) {
|
||||
UniValue o(UniValue::VOBJ);
|
||||
// Send the transaction
|
||||
if (!testmode) {
|
||||
CWalletTx wtx(pwalletMain, tx);
|
||||
if (!pwalletMain->CommitTransaction(wtx, reservekey)) {
|
||||
// More details in debug.log
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "SendTransaction: CommitTransaction failed");
|
||||
}
|
||||
o.pushKV("txid", tx.GetHash().ToString());
|
||||
} else {
|
||||
// Test mode does not send the transaction to the network.
|
||||
o.pushKV("test", 1);
|
||||
o.pushKV("txid", tx.GetHash().ToString());
|
||||
o.pushKV("hex", EncodeHexTx(tx));
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
std::pair<CTransaction, UniValue> SignSendRawTransaction(UniValue obj, std::optional<std::reference_wrapper<CReserveKey>> reservekey, bool testmode) {
|
||||
// Sign the raw transaction
|
||||
UniValue rawtxnValue = find_value(obj, "rawtxn");
|
||||
|
@ -55,7 +34,9 @@ std::pair<CTransaction, UniValue> SignSendRawTransaction(UniValue obj, std::opti
|
|||
CTransaction tx;
|
||||
stream >> tx;
|
||||
|
||||
UniValue sendResult = SendTransaction(tx, reservekey, testmode);
|
||||
// Recipient mappings are not available when sending a raw transaction.
|
||||
std::vector<RecipientMapping> recipientMappings;
|
||||
UniValue sendResult = SendTransaction(tx, recipientMappings, reservekey, testmode);
|
||||
|
||||
return std::make_pair(tx, sendResult);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
#ifndef ZCASH_WALLET_ASYNCRPCOPERATION_COMMON_H
|
||||
#define ZCASH_WALLET_ASYNCRPCOPERATION_COMMON_H
|
||||
|
||||
#include "core_io.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "rpc/protocol.h"
|
||||
#include "univalue.h"
|
||||
#include "wallet.h"
|
||||
|
||||
|
@ -13,19 +15,48 @@
|
|||
|
||||
/**
|
||||
* Sends a given transaction.
|
||||
*
|
||||
* If testmode is false, commit the transaction to the wallet,
|
||||
*
|
||||
* If testmode is false, commit the transaction to the wallet,
|
||||
* return {"txid": tx.GetHash().ToString()}
|
||||
*
|
||||
*
|
||||
* If testmode is true, do not commit the transaction,
|
||||
* return {"test": 1, "txid": tx.GetHash().ToString(), "hex": EncodeHexTx(tx)}
|
||||
*/
|
||||
UniValue SendTransaction(CTransaction& tx, std::optional<std::reference_wrapper<CReserveKey>> reservekey, bool testmode);
|
||||
template <typename RecipientMapping>
|
||||
UniValue SendTransaction(
|
||||
const CTransaction& tx,
|
||||
const std::vector<RecipientMapping>& recipients,
|
||||
std::optional<std::reference_wrapper<CReserveKey>> reservekey,
|
||||
bool testmode)
|
||||
{
|
||||
UniValue o(UniValue::VOBJ);
|
||||
// Send the transaction
|
||||
if (!testmode) {
|
||||
CWalletTx wtx(pwalletMain, tx);
|
||||
// save the mapping from (receiver, txid) to UA
|
||||
if (!pwalletMain->SaveRecipientMappings(tx.GetHash(), recipients)) {
|
||||
// More details in debug.log
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "SendTransaction: SaveRecipientMappings failed");
|
||||
}
|
||||
if (!pwalletMain->CommitTransaction(wtx, reservekey)) {
|
||||
// More details in debug.log
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "SendTransaction: CommitTransaction failed");
|
||||
}
|
||||
o.pushKV("txid", tx.GetHash().ToString());
|
||||
} else {
|
||||
// Test mode does not send the transaction to the network nor save the recipient mappings.
|
||||
o.pushKV("test", 1);
|
||||
o.pushKV("txid", tx.GetHash().ToString());
|
||||
o.pushKV("hex", EncodeHexTx(tx));
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sign and send a raw transaction.
|
||||
* Raw transaction as hex string should be in object field "rawtxn"
|
||||
*
|
||||
*
|
||||
* Returns a pair of (the parsed transaction, and the result of sending)
|
||||
*/
|
||||
std::pair<CTransaction, UniValue> SignSendRawTransaction(UniValue obj, std::optional<std::reference_wrapper<CReserveKey>> reservekey, bool testmode);
|
||||
|
|
|
@ -364,7 +364,9 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
|
|||
// Build the transaction
|
||||
tx_ = builder_.Build().GetTxOrThrow();
|
||||
|
||||
UniValue sendResult = SendTransaction(tx_, std::nullopt, testmode);
|
||||
// TODO: update this when unified address support is added
|
||||
std::vector<RecipientMapping> recipientMappings;
|
||||
UniValue sendResult = SendTransaction(tx_, recipientMappings, std::nullopt, testmode);
|
||||
set_result(sendResult);
|
||||
|
||||
return true;
|
||||
|
|
|
@ -464,7 +464,7 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
|||
auto buildResult = builder_.Build();
|
||||
auto tx = buildResult.GetTxOrThrow();
|
||||
|
||||
UniValue sendResult = SendTransaction(tx, std::nullopt, testmode);
|
||||
UniValue sendResult = SendTransaction(tx, recipients_, std::nullopt, testmode);
|
||||
set_result(sendResult);
|
||||
|
||||
return tx.GetHash();
|
||||
|
|
|
@ -25,14 +25,13 @@
|
|||
|
||||
using namespace libzcash;
|
||||
|
||||
class SendManyRecipient {
|
||||
class SendManyRecipient : public RecipientMapping {
|
||||
public:
|
||||
RecipientAddress address;
|
||||
CAmount amount;
|
||||
std::optional<std::string> memo;
|
||||
|
||||
SendManyRecipient(RecipientAddress address_, CAmount amount_, std::optional<std::string> memo_) :
|
||||
address(address_), amount(amount_), memo(memo_) {}
|
||||
SendManyRecipient(std::optional<libzcash::UnifiedAddress> ua_, libzcash::RecipientAddress address_, CAmount amount_, std::optional<std::string> memo_) :
|
||||
RecipientMapping(ua_, address_), amount(amount_), memo(memo_) {}
|
||||
};
|
||||
|
||||
class TxOutputAmounts {
|
||||
|
|
|
@ -213,6 +213,29 @@ bool AsyncRPCOperation_shieldcoinbase::main_impl() {
|
|||
return std::visit(ShieldToAddress(this, sendAmount), tozaddr_);
|
||||
}
|
||||
|
||||
void ShieldToAddress::shieldToAddress(const libzcash::RecipientAddress& recipient, AsyncRPCOperation_shieldcoinbase *m_op) {
|
||||
m_op->builder_.SetFee(m_op->fee_);
|
||||
|
||||
// Sending from a t-address, which we don't have an ovk for. Instead,
|
||||
// generate a common one from the HD seed. This ensures the data is
|
||||
// recoverable, while keeping it logically separate from the ZIP 32
|
||||
// Sapling key hierarchy, which the user might not be using.
|
||||
// FIXME: update to use the ZIP-316 OVK (#5511)
|
||||
HDSeed seed = pwalletMain->GetHDSeedForRPC();
|
||||
uint256 ovk = ovkForShieldingFromTaddr(seed);
|
||||
|
||||
// Add transparent inputs
|
||||
for (auto t : m_op->inputs_) {
|
||||
m_op->builder_.AddTransparentInput(COutPoint(t.txid, t.vout), t.scriptPubKey, t.amount);
|
||||
}
|
||||
|
||||
// Send all value to the target recipient
|
||||
m_op->builder_.SendChangeTo(recipient, ovk);
|
||||
|
||||
// Build the transaction
|
||||
m_op->tx_ = m_op->builder_.Build().GetTxOrThrow();
|
||||
}
|
||||
|
||||
bool ShieldToAddress::operator()(const CKeyID &addr) const {
|
||||
return false;
|
||||
}
|
||||
|
@ -250,30 +273,11 @@ bool ShieldToAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) co
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ShieldToAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) const {
|
||||
m_op->builder_.SetFee(m_op->fee_);
|
||||
ShieldToAddress::shieldToAddress(zaddr, m_op);
|
||||
|
||||
// Sending from a t-address, which we don't have an ovk for. Instead,
|
||||
// generate a common one from the HD seed. This ensures the data is
|
||||
// recoverable, while keeping it logically separate from the ZIP 32
|
||||
// Sapling key hierarchy, which the user might not be using.
|
||||
// FIXME: update to use the ZIP-316 OVK
|
||||
HDSeed seed = pwalletMain->GetHDSeedForRPC();
|
||||
uint256 ovk = ovkForShieldingFromTaddr(seed);
|
||||
|
||||
// Add transparent inputs
|
||||
for (auto t : m_op->inputs_) {
|
||||
m_op->builder_.AddTransparentInput(COutPoint(t.txid, t.vout), t.scriptPubKey, t.amount);
|
||||
}
|
||||
|
||||
// Send all value to the target z-addr
|
||||
m_op->builder_.SendChangeTo(zaddr, ovk);
|
||||
|
||||
// Build the transaction
|
||||
m_op->tx_ = m_op->builder_.Build().GetTxOrThrow();
|
||||
|
||||
UniValue sendResult = SendTransaction(m_op->tx_, std::nullopt, m_op->testmode);
|
||||
std::vector<RecipientMapping> recipientMappings;
|
||||
UniValue sendResult = SendTransaction(m_op->tx_, recipientMappings, std::nullopt, m_op->testmode);
|
||||
m_op->set_result(sendResult);
|
||||
|
||||
return true;
|
||||
|
@ -283,7 +287,13 @@ bool ShieldToAddress::operator()(const libzcash::UnifiedAddress &uaddr) const {
|
|||
// TODO check if an Orchard address is present, send to it if so.
|
||||
const auto receiver{uaddr.GetSaplingReceiver()};
|
||||
if (receiver.has_value()) {
|
||||
return ShieldToAddress(m_op, sendAmount)(receiver.value());
|
||||
ShieldToAddress::shieldToAddress(receiver.value(), m_op);
|
||||
|
||||
std::vector<RecipientMapping> recipientMappings = {RecipientMapping(uaddr, receiver.value())};
|
||||
UniValue sendResult = SendTransaction(m_op->tx_, recipientMappings, std::nullopt, m_op->testmode);
|
||||
m_op->set_result(sendResult);
|
||||
|
||||
return true;
|
||||
}
|
||||
// This UA must contain a transparent address, which can't be the destination of coinbase shielding.
|
||||
return false;
|
||||
|
|
|
@ -99,6 +99,8 @@ class ShieldToAddress
|
|||
private:
|
||||
AsyncRPCOperation_shieldcoinbase *m_op;
|
||||
CAmount sendAmount;
|
||||
|
||||
static void shieldToAddress(const libzcash::RecipientAddress& recipient, AsyncRPCOperation_shieldcoinbase *m_op);
|
||||
public:
|
||||
ShieldToAddress(AsyncRPCOperation_shieldcoinbase *op, CAmount sendAmount) :
|
||||
m_op(op), sendAmount(sendAmount) {}
|
||||
|
|
|
@ -547,6 +547,7 @@ UniValue dumpwallet(const UniValue& params, bool fHelp)
|
|||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"dumpwallet \"filename\"\n"
|
||||
"\nDEPRECATED. Please use the z_exportwallet RPC instead.\n"
|
||||
"\nDumps taddr wallet keys in a human-readable format. Overwriting an existing file is not permitted.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"filename\" (string, required) The filename, saved in folder set by zcashd -exportdir option\n"
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "primitives/transaction.h"
|
||||
#include "zcbenchmarks.h"
|
||||
#include "script/interpreter.h"
|
||||
#include "zcash/Zcash.h"
|
||||
#include "zcash/Address.hpp"
|
||||
#include "zcash/address/zip32.h"
|
||||
|
||||
|
@ -3483,6 +3484,30 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
|
|||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr.");
|
||||
}
|
||||
|
||||
// A non-unified address argument that is a receiver within a
|
||||
// unified address known to this wallet is not allowed.
|
||||
if (std::visit(match {
|
||||
[&](const CKeyID& addr) {
|
||||
return pwalletMain->FindUnifiedAddressByReceiver(addr).has_value();
|
||||
},
|
||||
[&](const CScriptID& addr) {
|
||||
return pwalletMain->FindUnifiedAddressByReceiver(addr).has_value();
|
||||
},
|
||||
[&](const libzcash::SaplingPaymentAddress& addr) {
|
||||
return pwalletMain->FindUnifiedAddressByReceiver(addr).has_value();
|
||||
},
|
||||
[&](const libzcash::SproutPaymentAddress& addr) {
|
||||
// A unified address can't contain a Sprout receiver.
|
||||
return false;
|
||||
},
|
||||
[&](const libzcash::UnifiedAddress& addr) {
|
||||
// We allow unified addresses themselves, which cannot recurse.
|
||||
return false;
|
||||
}
|
||||
}, decoded.value())) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "The provided address is a bare receiver from a Unified Address in this wallet. Provide the full UA instead.");
|
||||
}
|
||||
|
||||
// Visitor to support Sprout and Sapling addrs
|
||||
if (!std::visit(PaymentAddressBelongsToWallet(pwalletMain), decoded.value())) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found.");
|
||||
|
@ -3917,6 +3942,7 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp)
|
|||
if (fHelp || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"z_gettotalbalance ( minconf includeWatchonly )\n"
|
||||
"\nDEPRECATED. Please use the z_getbalanceforaccount RPC instead.\n"
|
||||
"\nReturn the total value of funds stored in the node's wallet.\n"
|
||||
"\nCAUTION: If the wallet contains any addresses for which it only has incoming viewing keys,"
|
||||
"\nthe returned private balance may be larger than the actual balance, because spends cannot"
|
||||
|
@ -4352,43 +4378,16 @@ size_t EstimateTxSize(
|
|||
int nextBlockHeight) {
|
||||
CMutableTransaction mtx;
|
||||
mtx.fOverwintered = true;
|
||||
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
|
||||
mtx.nVersion = SAPLING_TX_VERSION;
|
||||
mtx.nConsensusBranchId = CurrentEpochBranchId(nextBlockHeight, Params().GetConsensus());
|
||||
|
||||
bool fromTaddr = std::visit(match {
|
||||
[&](const AccountZTXOPattern& acct) {
|
||||
return
|
||||
acct.GetReceiverTypes().empty() ||
|
||||
acct.GetReceiverTypes().count(ReceiverType::P2PKH) > 0 ||
|
||||
acct.GetReceiverTypes().count(ReceiverType::P2SH) > 0;
|
||||
},
|
||||
[&](const CKeyID& keyId) {
|
||||
return true;
|
||||
},
|
||||
[&](const CScriptID& scriptId) {
|
||||
return true;
|
||||
},
|
||||
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
|
||||
return ufvk.GetTransparentKey().has_value();
|
||||
},
|
||||
[&](const libzcash::SproutPaymentAddress& addr) {
|
||||
return false;
|
||||
},
|
||||
[&](const libzcash::SproutViewingKey& addr) {
|
||||
return false;
|
||||
},
|
||||
[&](const libzcash::SaplingPaymentAddress& addr) {
|
||||
return false;
|
||||
},
|
||||
[&](const libzcash::SaplingExtendedFullViewingKey& addr) {
|
||||
return false;
|
||||
}
|
||||
}, ztxoSelector.GetPattern());
|
||||
bool fromSprout = ztxoSelector.SelectsSprout();
|
||||
bool fromTaddr = ztxoSelector.SelectsTransparent();
|
||||
|
||||
// As a sanity check, estimate and verify that the size of the transaction will be valid.
|
||||
// Depending on the input notes, the actual tx size may turn out to be larger and perhaps invalid.
|
||||
size_t txsize = 0;
|
||||
size_t taddrRecipientCount = 0;
|
||||
size_t orchardRecipientCount = 0;
|
||||
for (const SendManyRecipient& recipient : recipients) {
|
||||
std::visit(match {
|
||||
[&](const CKeyID&) {
|
||||
|
@ -4405,13 +4404,27 @@ size_t EstimateTxSize(
|
|||
jsdesc.proof = GrothProof();
|
||||
mtx.vJoinSplit.push_back(jsdesc);
|
||||
},
|
||||
[&](const libzcash::UnifiedAddress& ua) {
|
||||
// FIXME
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Unified addresses not yet supported.");
|
||||
[&](const libzcash::OrchardRawAddress& addr) {
|
||||
if (fromSprout) {
|
||||
throw JSONRPCError(
|
||||
RPC_INVALID_PARAMETER,
|
||||
"Sending funds from a Sprout address to a Unified Address is not supported by z_sendmany");
|
||||
}
|
||||
orchardRecipientCount += 1;
|
||||
}
|
||||
}, recipient.address);
|
||||
}
|
||||
|
||||
bool nu5Active = Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_NU5);
|
||||
|
||||
if (fromSprout || !nu5Active) {
|
||||
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
|
||||
mtx.nVersion = SAPLING_TX_VERSION;
|
||||
} else {
|
||||
mtx.nVersionGroupId = ZIP225_VERSION_GROUP_ID;
|
||||
mtx.nVersion = ZIP225_TX_VERSION;
|
||||
}
|
||||
|
||||
CTransaction tx(mtx);
|
||||
txsize += GetSerializeSize(tx, SER_NETWORK, tx.nVersion);
|
||||
if (fromTaddr) {
|
||||
|
@ -4420,6 +4433,12 @@ size_t EstimateTxSize(
|
|||
}
|
||||
txsize += CTXOUT_REGULAR_SIZE * taddrRecipientCount;
|
||||
|
||||
if (orchardRecipientCount > 0) {
|
||||
// - The Orchard transaction builder pads to a minimum of 2 actions.
|
||||
// - We subtract 1 because `GetSerializeSize(tx, ...)` already counts
|
||||
// `ZC_ZIP225_ORCHARD_NUM_ACTIONS_BASE_SIZE`.
|
||||
txsize += ZC_ZIP225_ORCHARD_BASE_SIZE - 1 + ZC_ZIP225_ORCHARD_MARGINAL_SIZE * std::max(2, (int) orchardRecipientCount);
|
||||
}
|
||||
return txsize;
|
||||
}
|
||||
|
||||
|
@ -4582,7 +4601,12 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
|
|||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amount must be positive");
|
||||
}
|
||||
|
||||
recipients.push_back(SendManyRecipient(addr.value(), nAmount, memo));
|
||||
std::optional<libzcash::UnifiedAddress> ua = std::nullopt;
|
||||
if (std::holds_alternative<libzcash::UnifiedAddress>(decoded.value())) {
|
||||
ua = std::get<libzcash::UnifiedAddress>(decoded.value());
|
||||
}
|
||||
|
||||
recipients.push_back(SendManyRecipient(ua, addr.value(), nAmount, memo));
|
||||
nTotalOut += nAmount;
|
||||
}
|
||||
if (recipients.empty()) {
|
||||
|
|
|
@ -1235,7 +1235,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
|
|||
{
|
||||
auto selector = pwalletMain->ZTXOSelectorForAddress(taddr1, true).value();
|
||||
TransactionBuilder builder(consensusParams, nHeight + 1, std::nullopt, pwalletMain);
|
||||
std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1, 100*COIN, "DEADBEEF") };
|
||||
std::vector<SendManyRecipient> recipients = { SendManyRecipient(std::nullopt, zaddr1, 100*COIN, "DEADBEEF") };
|
||||
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1));
|
||||
operation->main();
|
||||
BOOST_CHECK(operation->isFailed());
|
||||
|
@ -1247,7 +1247,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
|
|||
{
|
||||
auto selector = pwalletMain->ZTXOSelectorForAddress(zaddr1, true).value();
|
||||
TransactionBuilder builder(consensusParams, nHeight + 1, std::nullopt, pwalletMain);
|
||||
std::vector<SendManyRecipient> recipients = { SendManyRecipient(taddr1, 100*COIN, "DEADBEEF") };
|
||||
std::vector<SendManyRecipient> recipients = { SendManyRecipient(std::nullopt, taddr1, 100*COIN, "DEADBEEF") };
|
||||
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1));
|
||||
operation->main();
|
||||
BOOST_CHECK(operation->isFailed());
|
||||
|
@ -1259,7 +1259,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
|
|||
{
|
||||
auto selector = pwalletMain->ZTXOSelectorForAddress(zaddr1, true).value();
|
||||
TransactionBuilder builder(consensusParams, nHeight + 1, std::nullopt, pwalletMain);
|
||||
std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1, 100*COIN, "DEADBEEF") };
|
||||
std::vector<SendManyRecipient> recipients = { SendManyRecipient(std::nullopt, zaddr1, 100*COIN, "DEADBEEF") };
|
||||
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 1));
|
||||
std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation);
|
||||
TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr);
|
||||
|
@ -1359,7 +1359,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling)
|
|||
mtx = CreateNewContextualCMutableTransaction(consensusParams, nextBlockHeight);
|
||||
|
||||
auto selector = pwalletMain->ZTXOSelectorForAddress(taddr, true).value();
|
||||
std::vector<SendManyRecipient> recipients = { SendManyRecipient(pa, 1*COIN, "ABCD") };
|
||||
std::vector<SendManyRecipient> recipients = { SendManyRecipient(std::nullopt, pa, 1*COIN, "ABCD") };
|
||||
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(std::move(builder), selector, recipients, 0));
|
||||
std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation);
|
||||
|
||||
|
|
|
@ -6744,7 +6744,7 @@ std::optional<libzcash::UnifiedAddress> UnifiedAddressForReceiver::operator()(co
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool ZTXOSelector::SelectsTransparent() {
|
||||
bool ZTXOSelector::SelectsTransparent() const {
|
||||
return std::visit(match {
|
||||
[](const CKeyID& keyId) { return true; },
|
||||
[](const CScriptID& scriptId) { return true; },
|
||||
|
@ -6756,14 +6756,14 @@ bool ZTXOSelector::SelectsTransparent() {
|
|||
[](const AccountZTXOPattern& acct) { return acct.IncludesP2PKH() || acct.IncludesP2SH(); }
|
||||
}, this->pattern);
|
||||
}
|
||||
bool ZTXOSelector::SelectsSprout() {
|
||||
bool ZTXOSelector::SelectsSprout() const {
|
||||
return std::visit(match {
|
||||
[](const libzcash::SproutViewingKey& addr) { return true; },
|
||||
[](const libzcash::SproutPaymentAddress& extfvk) { return true; },
|
||||
[](const auto& addr) { return false; }
|
||||
}, this->pattern);
|
||||
}
|
||||
bool ZTXOSelector::SelectsSapling() {
|
||||
bool ZTXOSelector::SelectsSapling() const {
|
||||
return std::visit(match {
|
||||
[](const libzcash::SaplingPaymentAddress& addr) { return true; },
|
||||
[](const libzcash::SaplingExtendedSpendingKey& extfvk) { return true; },
|
||||
|
|
|
@ -144,6 +144,15 @@ struct CRecipient
|
|||
bool fSubtractFeeFromAmount;
|
||||
};
|
||||
|
||||
class RecipientMapping {
|
||||
public:
|
||||
std::optional<libzcash::UnifiedAddress> ua;
|
||||
libzcash::RecipientAddress address;
|
||||
|
||||
RecipientMapping(std::optional<libzcash::UnifiedAddress> ua_, libzcash::RecipientAddress address_) :
|
||||
ua(ua_), address(address_) {}
|
||||
};
|
||||
|
||||
typedef std::map<std::string, std::string> mapValue_t;
|
||||
|
||||
|
||||
|
@ -784,9 +793,9 @@ public:
|
|||
return requireSpendingKeys;
|
||||
}
|
||||
|
||||
bool SelectsTransparent();
|
||||
bool SelectsSprout();
|
||||
bool SelectsSapling();
|
||||
bool SelectsTransparent() const;
|
||||
bool SelectsSprout() const;
|
||||
bool SelectsSapling() const;
|
||||
};
|
||||
|
||||
class SpendableInputs {
|
||||
|
@ -1572,6 +1581,32 @@ public:
|
|||
*/
|
||||
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet,
|
||||
std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true);
|
||||
|
||||
/**
|
||||
* Save a set of (txid, RecipientAddress, std::optional<UnifiedAddress>) mappings to the wallet.
|
||||
* This information is persisted so that it's possible to correctly display the unified
|
||||
* address to which a payment was sent.
|
||||
*/
|
||||
template <typename RecipientMapping>
|
||||
bool SaveRecipientMappings(const uint256& txid, const std::vector<RecipientMapping>& recipients)
|
||||
{
|
||||
LOCK2(cs_main, cs_wallet);
|
||||
LogPrintf("SaveRecipientMappings:\n%s", txid.ToString());
|
||||
|
||||
for (const auto& recipient : recipients)
|
||||
{
|
||||
if (recipient.ua.has_value()) {
|
||||
CWalletDB(strWalletFile).WriteRecipientMapping(
|
||||
txid,
|
||||
recipient.address,
|
||||
recipient.ua.value()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommitTransaction(CWalletTx& wtxNew, std::optional<std::reference_wrapper<CReserveKey>> reservekey);
|
||||
|
||||
static CFeeRate minTxFee;
|
||||
|
@ -1965,5 +2000,4 @@ public:
|
|||
std::optional<libzcash::UnifiedAddress> operator()(const libzcash::UnknownReceiver& receiver) const;
|
||||
};
|
||||
|
||||
|
||||
#endif // BITCOIN_WALLET_WALLET_H
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "proof_verifier.h"
|
||||
#include "protocol.h"
|
||||
#include "serialize.h"
|
||||
#include "script/standard.h"
|
||||
#include "sync.h"
|
||||
#include "util.h"
|
||||
#include "utiltime.h"
|
||||
|
@ -313,6 +314,19 @@ bool CWalletDB::WriteMinVersion(int nVersion)
|
|||
return Write(std::string("minversion"), nVersion);
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteRecipientMapping(const uint256& txid, const libzcash::RecipientAddress& address, const libzcash::UnifiedAddress& ua)
|
||||
{
|
||||
auto recipientReceiver = libzcash::RecipientAddressToReceiver(address);
|
||||
// Check that recipient address exists in given UA.
|
||||
if (!ua.ContainsReceiver(recipientReceiver)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<uint256, CSerializeRecipientAddress> key = std::make_pair(txid, CSerializeRecipientAddress(address));
|
||||
std::string uaString = KeyIO(Params()).EncodePaymentAddress(ua);
|
||||
return Write(std::make_pair(std::string("recipientmapping"), key), uaString);
|
||||
}
|
||||
|
||||
class CWalletScanState {
|
||||
public:
|
||||
unsigned int nKeys;
|
||||
|
@ -847,6 +861,27 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
|||
return false;
|
||||
}
|
||||
}
|
||||
else if (strType == "recipientmapping")
|
||||
{
|
||||
uint256 txid;
|
||||
std::string rawUa;
|
||||
ssKey >> txid;
|
||||
auto recipient = CSerializeRecipientAddress::Read(ssKey);
|
||||
ssValue >> rawUa;
|
||||
|
||||
auto ua = libzcash::UnifiedAddress::Parse(Params(), rawUa);
|
||||
if (!ua.has_value()) {
|
||||
strErr = "Error in wallet database: non-UnifiedAddress in recipientmapping";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto recipientReceiver = libzcash::RecipientAddressToReceiver(recipient);
|
||||
|
||||
if (!ua.value().ContainsReceiver(recipientReceiver)) {
|
||||
strErr = "Error in wallet database: recipientmapping UA does not contain recipient";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (...)
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -314,6 +314,73 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// Serialization wrapper for reading and writing RecipientAddress
|
||||
// as a pair of typecode and address bytes, similar to how unified address
|
||||
// receivers are written (but excluding the unknown receiver case)
|
||||
class CSerializeRecipientAddress {
|
||||
libzcash::RecipientAddress recipient;
|
||||
libzcash::ReceiverType typecode;
|
||||
CSerializeRecipientAddress() {} // for serialization only
|
||||
|
||||
public:
|
||||
CSerializeRecipientAddress(libzcash::RecipientAddress recipient): recipient(recipient) {}
|
||||
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s) const {
|
||||
std::visit(match {
|
||||
[&](const CKeyID& keyId) {
|
||||
ReceiverTypeSer(libzcash::ReceiverType::P2PKH).Serialize(s);
|
||||
s << keyId;
|
||||
},
|
||||
[&](const CScriptID& scriptId) {
|
||||
ReceiverTypeSer(libzcash::ReceiverType::P2SH).Serialize(s);
|
||||
s << scriptId;
|
||||
},
|
||||
[&](const libzcash::SaplingPaymentAddress& saplingAddr) {
|
||||
ReceiverTypeSer(libzcash::ReceiverType::Sapling).Serialize(s);
|
||||
s << saplingAddr;
|
||||
}
|
||||
}, recipient);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s) {
|
||||
// This cast is fine because ZIP 316 uses CompactSize serialization including the
|
||||
// size limit, which means it is at most a uint32_t.
|
||||
typecode = (libzcash::ReceiverType) ReadCompactSize(s);
|
||||
switch (typecode) {
|
||||
case libzcash::ReceiverType::P2PKH: {
|
||||
CKeyID key;
|
||||
s >> key;
|
||||
recipient = key;
|
||||
break;
|
||||
}
|
||||
case libzcash::ReceiverType::P2SH: {
|
||||
CScriptID script;
|
||||
s >> script;
|
||||
recipient = script;
|
||||
break;
|
||||
}
|
||||
case libzcash::ReceiverType::Sapling: {
|
||||
libzcash::SaplingPaymentAddress saplingAddr;
|
||||
s >> saplingAddr;
|
||||
recipient = saplingAddr;
|
||||
break;
|
||||
}
|
||||
case libzcash::ReceiverType::Orchard: {
|
||||
// TODO ORCHARD: Handle when we add Orchard to RecipientAddress
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
static libzcash::RecipientAddress Read(Stream& stream) {
|
||||
CSerializeRecipientAddress csr;
|
||||
stream >> csr;
|
||||
return csr.recipient;
|
||||
}
|
||||
};
|
||||
|
||||
/** Access to the wallet database */
|
||||
class CWalletDB : public CDB
|
||||
|
@ -356,6 +423,8 @@ public:
|
|||
|
||||
bool WriteMinVersion(int nVersion);
|
||||
|
||||
bool WriteRecipientMapping(const uint256& txid, const libzcash::RecipientAddress& address, const libzcash::UnifiedAddress& ua);
|
||||
|
||||
/// Write destination data key,value tuple to database
|
||||
bool WriteDestData(const std::string &address, const std::string &key, const std::string &value);
|
||||
/// Erase destination data tuple from wallet database
|
||||
|
|
|
@ -16,6 +16,81 @@ namespace libzcash {
|
|||
// Unified Addresses
|
||||
//
|
||||
|
||||
static bool AddOrchardReceiver(void* ua, OrchardRawAddressPtr* ptr)
|
||||
{
|
||||
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(
|
||||
libzcash::OrchardRawAddress::KeyIoOnlyFromReceiver(ptr));
|
||||
}
|
||||
|
||||
/**
|
||||
* `raw` MUST be 43 bytes.
|
||||
*/
|
||||
static bool AddSaplingReceiver(void* ua, const unsigned char* raw)
|
||||
{
|
||||
CDataStream ss(
|
||||
reinterpret_cast<const char*>(raw),
|
||||
reinterpret_cast<const char*>(raw + 43),
|
||||
SER_NETWORK,
|
||||
PROTOCOL_VERSION);
|
||||
libzcash::SaplingPaymentAddress receiver;
|
||||
ss >> receiver;
|
||||
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
|
||||
}
|
||||
|
||||
/**
|
||||
* `raw` MUST be 20 bytes.
|
||||
*/
|
||||
static bool AddP2SHReceiver(void* ua, const unsigned char* raw)
|
||||
{
|
||||
CDataStream ss(
|
||||
reinterpret_cast<const char*>(raw),
|
||||
reinterpret_cast<const char*>(raw + 20),
|
||||
SER_NETWORK,
|
||||
PROTOCOL_VERSION);
|
||||
CScriptID receiver;
|
||||
ss >> receiver;
|
||||
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
|
||||
}
|
||||
|
||||
/**
|
||||
* `raw` MUST be 20 bytes.
|
||||
*/
|
||||
static bool AddP2PKHReceiver(void* ua, const unsigned char* raw)
|
||||
{
|
||||
CDataStream ss(
|
||||
reinterpret_cast<const char*>(raw),
|
||||
reinterpret_cast<const char*>(raw + 20),
|
||||
SER_NETWORK,
|
||||
PROTOCOL_VERSION);
|
||||
CKeyID receiver;
|
||||
ss >> receiver;
|
||||
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
|
||||
}
|
||||
|
||||
static bool AddUnknownReceiver(void* ua, uint32_t typecode, const unsigned char* data, size_t len)
|
||||
{
|
||||
libzcash::UnknownReceiver receiver(typecode, std::vector(data, data + len));
|
||||
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
|
||||
}
|
||||
|
||||
std::optional<UnifiedAddress> UnifiedAddress::Parse(const KeyConstants& keyConstants, const std::string& str) {
|
||||
libzcash::UnifiedAddress ua;
|
||||
if (zcash_address_parse_unified(
|
||||
str.c_str(),
|
||||
keyConstants.NetworkIDString().c_str(),
|
||||
&ua,
|
||||
AddOrchardReceiver,
|
||||
AddSaplingReceiver,
|
||||
AddP2SHReceiver,
|
||||
AddP2PKHReceiver,
|
||||
AddUnknownReceiver)
|
||||
) {
|
||||
return ua;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<const Receiver*> UnifiedAddress::GetSorted() const {
|
||||
std::vector<const libzcash::Receiver*> sorted;
|
||||
for (const auto& receiver : receivers) {
|
||||
|
@ -43,6 +118,15 @@ bool UnifiedAddress::AddReceiver(Receiver receiver) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool UnifiedAddress::ContainsReceiver(const Receiver& receiver) const {
|
||||
for (const auto& r : GetReceiversAsParsed()) {
|
||||
if (r == receiver) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<CKeyID> UnifiedAddress::GetP2PKHReceiver() const {
|
||||
for (const auto& r : receivers) {
|
||||
if (std::holds_alternative<CKeyID>(r)) {
|
||||
|
|
|
@ -66,6 +66,15 @@ class UnifiedAddress {
|
|||
public:
|
||||
UnifiedAddress() {}
|
||||
|
||||
static std::optional<UnifiedAddress> Parse(const KeyConstants& keyConstants, const std::string& str);
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(receivers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given receiver to this unified address.
|
||||
*
|
||||
|
@ -78,6 +87,8 @@ public:
|
|||
*/
|
||||
bool AddReceiver(Receiver receiver);
|
||||
|
||||
bool ContainsReceiver(const Receiver& receiver) const;
|
||||
|
||||
const std::vector<Receiver>& GetReceiversAsParsed() const { return receivers; }
|
||||
|
||||
std::set<ReceiverType> GetKnownReceiverTypes() const {
|
||||
|
|
|
@ -28,4 +28,20 @@
|
|||
#define ZC_SAPLING_ENCCIPHERTEXT_SIZE (ZC_SAPLING_ENCPLAINTEXT_SIZE + NOTEENCRYPTION_AUTH_BYTES)
|
||||
#define ZC_SAPLING_OUTCIPHERTEXT_SIZE (ZC_SAPLING_OUTPLAINTEXT_SIZE + NOTEENCRYPTION_AUTH_BYTES)
|
||||
|
||||
// - CompactSize is at least 1 byte
|
||||
#define ZC_ZIP225_ORCHARD_NUM_ACTIONS_SIZE 1
|
||||
#define ZC_ZIP225_ORCHARD_FLAGS_SIZE 1
|
||||
#define ZC_ZIP225_ORCHARD_VALUE_BALANCE_SIZE 8
|
||||
#define ZC_ZIP225_ORCHARD_ANCHOR_SIZE 32
|
||||
// - CompactSize is at least 2 bytes because sizeProofsOrchard >= 253
|
||||
#define ZC_ZIP225_ORCHARD_SIZE_PROOFS_BASE_SIZE 2
|
||||
#define ZC_ZIP225_ORCHARD_PROOF_BASE_SIZE 2720
|
||||
#define ZC_ZIP225_ORCHARD_BINDING_SIG_SIZE 64
|
||||
#define ZC_ZIP225_ORCHARD_BASE_SIZE (ZC_ZIP225_ORCHARD_NUM_ACTIONS_SIZE + ZC_ZIP225_ORCHARD_FLAGS_SIZE + ZC_ZIP225_ORCHARD_VALUE_BALANCE_SIZE + ZC_ZIP225_ORCHARD_ANCHOR_SIZE + ZC_ZIP225_ORCHARD_SIZE_PROOFS_BASE_SIZE + ZC_ZIP225_ORCHARD_PROOF_BASE_SIZE + ZC_ZIP225_ORCHARD_BINDING_SIG_SIZE)
|
||||
// Marginal transaction size per Orchard Action
|
||||
#define ZC_ZIP225_ORCHARD_ACTION_SIZE 820
|
||||
#define ZC_ZIP225_ORCHARD_SPEND_AUTH_SIG_SIZE 64
|
||||
#define ZC_ZIP225_ORCHARD_PROOF_MARGINAL_SIZE 2272
|
||||
#define ZC_ZIP225_ORCHARD_MARGINAL_SIZE (ZC_ZIP225_ORCHARD_ACTION_SIZE + ZC_ZIP225_ORCHARD_SPEND_AUTH_SIG_SIZE + ZC_ZIP225_ORCHARD_PROOF_MARGINAL_SIZE)
|
||||
|
||||
#endif // ZCASH_ZCASH_ZCASH_H
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "zcash/Address.hpp"
|
||||
#include "unified.h"
|
||||
#include "util/match.h"
|
||||
|
||||
#include <rust/unified_keys.h>
|
||||
|
||||
|
@ -29,6 +30,14 @@ bool libzcash::HasTransparent(const std::set<ReceiverType>& receiverTypes) {
|
|||
return std::find_if(receiverTypes.begin(), receiverTypes.end(), has_transparent) != receiverTypes.end();
|
||||
}
|
||||
|
||||
Receiver libzcash::RecipientAddressToReceiver(const RecipientAddress& recipient) {
|
||||
return std::visit(match {
|
||||
[](const CKeyID& key) { return Receiver(key); },
|
||||
[](const CScriptID& scriptId) { return Receiver(scriptId); },
|
||||
[](const libzcash::SaplingPaymentAddress& addr) { return Receiver(addr); }
|
||||
}, recipient);
|
||||
}
|
||||
|
||||
std::optional<ZcashdUnifiedSpendingKey> ZcashdUnifiedSpendingKey::ForAccount(
|
||||
const HDSeed& seed,
|
||||
uint32_t bip44CoinType,
|
||||
|
|
|
@ -120,6 +120,8 @@ typedef std::variant<
|
|||
CKeyID,
|
||||
UnknownReceiver> Receiver;
|
||||
|
||||
Receiver RecipientAddressToReceiver(const RecipientAddress& recipient);
|
||||
|
||||
/**
|
||||
* An internal identifier for a unified full viewing key, derived as a
|
||||
* blake2b hash of the serialized form of the UFVK.
|
||||
|
|
Loading…
Reference in New Issue