Auto merge of #4560 - str4d:zip-207, r=nuttycom

[ZIP 207] Funding Streams + [ZIP 214] Dev Fund Recipients

Closes #4554. Closes #4555.
This commit is contained in:
Homu 2020-07-10 22:45:11 +00:00
commit 959f5b09d0
46 changed files with 1481 additions and 507 deletions

View File

@ -82,6 +82,7 @@ testScripts=(
'sprout_sapling_migration.py' 'sprout_sapling_migration.py'
'turnstile.py' 'turnstile.py'
'mining_shielded_coinbase.py' 'mining_shielded_coinbase.py'
'coinbase_funding_streams.py'
'framework.py' 'framework.py'
'sapling_rewind_check.py' 'sapling_rewind_check.py'
'feature_zip221.py' 'feature_zip221.py'

View File

@ -40,11 +40,11 @@ class BlockchainTest(BitcoinTestFramework):
node = self.nodes[0] node = self.nodes[0]
res = node.gettxoutsetinfo() res = node.gettxoutsetinfo()
assert_equal(res['total_amount'], decimal.Decimal('2181.25000000')) # 150*12.5 + 49*6.25 assert_equal(res['total_amount'], decimal.Decimal('2143.75000000')) # 144*12.5 + 55*6.25
assert_equal(res['transactions'], 200) assert_equal(res['transactions'], 200)
assert_equal(res['height'], 200) assert_equal(res['height'], 200)
assert_equal(res['txouts'], 349) # 150*2 + 49 assert_equal(res['txouts'], 343) # 144*2 + 55
assert_equal(res['bytes_serialized'], 14951), # 32*199 + 48*90 + 49*60 + 27*49 assert_equal(res['bytes_serialized'], 14819), # 32*199 + 48*90 + 49*54 + 27*55
assert_equal(len(res['bestblock']), 64) assert_equal(len(res['bestblock']), 64)
assert_equal(len(res['hash_serialized']), 64) assert_equal(len(res['hash_serialized']), 64)

View File

@ -0,0 +1,101 @@
#!/usr/bin/env python3
# Copyright (c) 2020 The Zcash developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://www.opensource.org/licenses/mit-license.php .
from test_framework.test_framework import BitcoinTestFramework
from test_framework.mininode import (
nuparams,
fundingstream,
)
from test_framework.util import (
assert_equal,
bitcoind_processes,
connect_nodes,
initialize_chain_clean,
start_node,
BLOSSOM_BRANCH_ID,
HEARTWOOD_BRANCH_ID,
CANOPY_BRANCH_ID,
)
class CoinbaseFundingStreamsTest (BitcoinTestFramework):
def setup_chain(self):
print("Initializing test directory "+self.options.tmpdir)
initialize_chain_clean(self.options.tmpdir, 4)
def start_node_with(self, index, extra_args=[]):
args = [
nuparams(BLOSSOM_BRANCH_ID, 1),
nuparams(HEARTWOOD_BRANCH_ID, 2),
nuparams(CANOPY_BRANCH_ID, 5),
"-nurejectoldversions=false",
]
return start_node(index, self.options.tmpdir, args + extra_args)
def setup_network(self, split=False):
self.nodes = []
self.nodes.append(self.start_node_with(0))
self.nodes.append(self.start_node_with(1))
connect_nodes(self.nodes[1], 0)
self.is_network_split=False
self.sync_all()
def run_test (self):
# Generate a shielded address for node 1 for miner rewards,
miner_addr = self.nodes[1].z_getnewaddress('sapling')
# Generate a shielded address (belonging to node 0) for funding stream
# rewards.
fs_addr = self.nodes[0].z_getnewaddress('sapling')
# Generate past heartwood activation we won't need node 1 from this
# point onward except to check miner reward balances
self.nodes[1].generate(2)
self.sync_all()
# Restart node 0 with funding streams.
self.nodes[0].stop()
bitcoind_processes[0].wait()
self.nodes[0] = self.start_node_with(0, [
"-mineraddress=%s" % miner_addr,
"-minetolocalwallet=0",
fundingstream(0, 5, 9, [fs_addr, fs_addr, fs_addr]),
fundingstream(1, 5, 9, [fs_addr, fs_addr, fs_addr]),
fundingstream(2, 5, 9, [fs_addr, fs_addr, fs_addr]),
])
connect_nodes(self.nodes[1], 0)
self.sync_all()
print("Generate to just prior to Canopy activation")
self.nodes[0].generate(2)
self.sync_all()
# All miner addresses belong to node 1; check balances
walletinfo = self.nodes[1].getwalletinfo()
assert_equal(walletinfo['immature_balance'], 10)
assert_equal(walletinfo['balance'], 0)
assert_equal(self.nodes[1].z_getbalance(miner_addr, 0), 10)
assert_equal(self.nodes[1].z_getbalance(miner_addr), 10)
print("Activating Canopy")
self.nodes[0].generate(4)
self.sync_all()
# check that miner payments made it to node 1's wallet
walletinfo = self.nodes[1].getwalletinfo()
assert_equal(walletinfo['immature_balance'], 10)
assert_equal(walletinfo['balance'], 0)
assert_equal(self.nodes[1].z_getbalance(miner_addr, 0), 30)
assert_equal(self.nodes[1].z_getbalance(miner_addr), 30)
# check that the node 0 private balance has been augmented by the
# funding stream payments
assert_equal(self.nodes[0].z_getbalance(fs_addr, 0), 5)
assert_equal(self.nodes[0].z_getbalance(fs_addr), 5)
assert_equal(self.nodes[0].z_gettotalbalance()['private'], '5.00')
assert_equal(self.nodes[0].z_gettotalbalance()['total'], '5.00')
if __name__ == '__main__':
CoinbaseFundingStreamsTest().main()

View File

@ -6,7 +6,10 @@
from decimal import Decimal from decimal import Decimal
from test_framework.authproxy import JSONRPCException from test_framework.authproxy import JSONRPCException
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.mininode import nuparams
from test_framework.util import ( from test_framework.util import (
BLOSSOM_BRANCH_ID,
HEARTWOOD_BRANCH_ID,
assert_equal, assert_equal,
assert_raises, assert_raises,
bitcoind_processes, bitcoind_processes,
@ -25,8 +28,8 @@ class ShieldCoinbaseTest (BitcoinTestFramework):
def start_node_with(self, index, extra_args=[]): def start_node_with(self, index, extra_args=[]):
args = [ args = [
"-nuparams=2bb40e60:1", # Blossom nuparams(BLOSSOM_BRANCH_ID, 1),
"-nuparams=f5b9230b:10", # Heartwood nuparams(HEARTWOOD_BRANCH_ID, 10),
"-nurejectoldversions=false", "-nurejectoldversions=false",
] ]
return start_node(index, self.options.tmpdir, args + extra_args) return start_node(index, self.options.tmpdir, args + extra_args)

View File

@ -80,6 +80,9 @@ def hash256(s):
def nuparams(branch_id, height): def nuparams(branch_id, height):
return '-nuparams=%x:%d' % (branch_id, height) return '-nuparams=%x:%d' % (branch_id, height)
def fundingstream(idx, start_height, end_height, addrs):
return '-fundingstream=%d:%d:%d:%s' % (idx, start_height, end_height, ",".join(addrs))
def ser_compactsize(n): def ser_compactsize(n):
if n < 253: if n < 253:
return struct.pack("B", n) return struct.pack("B", n)

View File

@ -181,6 +181,7 @@ BITCOIN_CORE_H = \
compat/sanity.h \ compat/sanity.h \
compressor.h \ compressor.h \
consensus/consensus.h \ consensus/consensus.h \
consensus/funding.h \
consensus/params.h \ consensus/params.h \
consensus/upgrades.h \ consensus/upgrades.h \
consensus/validation.h \ consensus/validation.h \
@ -413,6 +414,7 @@ libbitcoin_common_a_SOURCES = \
chainparams.cpp \ chainparams.cpp \
coins.cpp \ coins.cpp \
compressor.cpp \ compressor.cpp \
consensus/funding.cpp \
consensus/params.cpp \ consensus/params.cpp \
consensus/upgrades.cpp \ consensus/upgrades.cpp \
core_read.cpp \ core_read.cpp \

View File

@ -240,9 +240,11 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn
if (!ParseMoney(strValue, value)) if (!ParseMoney(strValue, value))
throw std::runtime_error("invalid TX output value"); throw std::runtime_error("invalid TX output value");
KeyIO keyIO(Params());
// extract and validate ADDRESS // extract and validate ADDRESS
std::string strAddr = strInput.substr(pos + 1, std::string::npos); std::string strAddr = strInput.substr(pos + 1, std::string::npos);
CTxDestination destination = DecodeDestination(strAddr); CTxDestination destination = keyIO.DecodeDestination(strAddr);
if (!IsValidDestination(destination)) { if (!IsValidDestination(destination)) {
throw std::runtime_error("invalid TX output address"); throw std::runtime_error("invalid TX output address");
} }
@ -399,10 +401,12 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& strInput)
UniValue keysObj = registers["privatekeys"]; UniValue keysObj = registers["privatekeys"];
fGivenKeys = true; fGivenKeys = true;
KeyIO keyIO(Params());
for (size_t kidx = 0; kidx < keysObj.size(); kidx++) { for (size_t kidx = 0; kidx < keysObj.size(); kidx++) {
if (!keysObj[kidx].isStr()) if (!keysObj[kidx].isStr())
throw std::runtime_error("privatekey not a std::string"); throw std::runtime_error("privatekey not a std::string");
CKey key = DecodeSecret(keysObj[kidx].getValStr()); CKey key = keyIO.DecodeSecret(keysObj[kidx].getValStr());
if (!key.IsValid()) { if (!key.IsValid()) {
throw std::runtime_error("privatekey not valid"); throw std::runtime_error("privatekey not valid");
} }

View File

@ -1,5 +1,6 @@
// Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers // Copyright (c) 2009-2014 The Bitcoin Core developers
// Copyright (c) 2015-2020 The Zcash Developers
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php . // file COPYING or https://www.opensource.org/licenses/mit-license.php .
@ -87,7 +88,7 @@ public:
consensus.fCoinbaseMustBeShielded = true; consensus.fCoinbaseMustBeShielded = true;
consensus.nSubsidySlowStartInterval = 20000; consensus.nSubsidySlowStartInterval = 20000;
consensus.nPreBlossomSubsidyHalvingInterval = Consensus::PRE_BLOSSOM_HALVING_INTERVAL; consensus.nPreBlossomSubsidyHalvingInterval = Consensus::PRE_BLOSSOM_HALVING_INTERVAL;
consensus.nPostBlossomSubsidyHalvingInterval = Consensus::POST_BLOSSOM_HALVING_INTERVAL; consensus.nPostBlossomSubsidyHalvingInterval = POST_BLOSSOM_HALVING_INTERVAL(Consensus::PRE_BLOSSOM_HALVING_INTERVAL);
consensus.nMajorityEnforceBlockUpgrade = 750; consensus.nMajorityEnforceBlockUpgrade = 750;
consensus.nMajorityRejectBlockOutdated = 950; consensus.nMajorityRejectBlockOutdated = 950;
consensus.nMajorityWindow = 4000; consensus.nMajorityWindow = 4000;
@ -125,6 +126,50 @@ public:
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight = consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight =
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
consensus.nFundingPeriodLength = consensus.nPostBlossomSubsidyHalvingInterval / 48;
// guarantees the first 2 characters, when base58 encoded, are "t1"
keyConstants.base58Prefixes[PUBKEY_ADDRESS] = {0x1C,0xB8};
// guarantees the first 2 characters, when base58 encoded, are "t3"
keyConstants.base58Prefixes[SCRIPT_ADDRESS] = {0x1C,0xBD};
// the first character, when base58 encoded, is "5" or "K" or "L" (as in Bitcoin)
keyConstants.base58Prefixes[SECRET_KEY] = {0x80};
// do not rely on these BIP32 prefixes; they are not specified and may change
keyConstants.base58Prefixes[EXT_PUBLIC_KEY] = {0x04,0x88,0xB2,0x1E};
keyConstants.base58Prefixes[EXT_SECRET_KEY] = {0x04,0x88,0xAD,0xE4};
// guarantees the first 2 characters, when base58 encoded, are "zc"
keyConstants.base58Prefixes[ZCPAYMENT_ADDRESS] = {0x16,0x9A};
// guarantees the first 4 characters, when base58 encoded, are "ZiVK"
keyConstants.base58Prefixes[ZCVIEWING_KEY] = {0xA8,0xAB,0xD3};
// guarantees the first 2 characters, when base58 encoded, are "SK"
keyConstants.base58Prefixes[ZCSPENDING_KEY] = {0xAB,0x36};
keyConstants.bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "zs";
keyConstants.bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviews";
keyConstants.bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivks";
keyConstants.bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-main";
keyConstants.bech32HRPs[SAPLING_EXTENDED_FVK] = "zxviews";
// TODO: This `if` can be removed once canopy activation height is set.
if (consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight != Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT) {
std::vector<std::string> addresses(48, "");
consensus.AddZIP207FundingStream(
keyConstants,
Consensus::FS_ZIP214_ECC,
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight, 2726400,
addresses);
consensus.AddZIP207FundingStream(
keyConstants,
Consensus::FS_ZIP214_ZF,
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight, 2726400,
addresses);
consensus.AddZIP207FundingStream(
keyConstants,
Consensus::FS_ZIP214_MG,
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight, 2726400,
addresses);
}
// The best chain should have at least this much work. // The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("000000000000000000000000000000000000000000000000017e73a331fae01c"); consensus.nMinimumChainWork = uint256S("000000000000000000000000000000000000000000000000017e73a331fae01c");
@ -155,28 +200,6 @@ public:
vSeeds.push_back(CDNSSeedData("zfnd.org", "mainnet.seeder.zfnd.org")); // Zcash Foundation vSeeds.push_back(CDNSSeedData("zfnd.org", "mainnet.seeder.zfnd.org")); // Zcash Foundation
vSeeds.push_back(CDNSSeedData("yolo.money", "mainnet.is.yolo.money")); // gtank vSeeds.push_back(CDNSSeedData("yolo.money", "mainnet.is.yolo.money")); // gtank
// guarantees the first 2 characters, when base58 encoded, are "t1"
base58Prefixes[PUBKEY_ADDRESS] = {0x1C,0xB8};
// guarantees the first 2 characters, when base58 encoded, are "t3"
base58Prefixes[SCRIPT_ADDRESS] = {0x1C,0xBD};
// the first character, when base58 encoded, is "5" or "K" or "L" (as in Bitcoin)
base58Prefixes[SECRET_KEY] = {0x80};
// do not rely on these BIP32 prefixes; they are not specified and may change
base58Prefixes[EXT_PUBLIC_KEY] = {0x04,0x88,0xB2,0x1E};
base58Prefixes[EXT_SECRET_KEY] = {0x04,0x88,0xAD,0xE4};
// guarantees the first 2 characters, when base58 encoded, are "zc"
base58Prefixes[ZCPAYMENT_ADDRRESS] = {0x16,0x9A};
// guarantees the first 4 characters, when base58 encoded, are "ZiVK"
base58Prefixes[ZCVIEWING_KEY] = {0xA8,0xAB,0xD3};
// guarantees the first 2 characters, when base58 encoded, are "SK"
base58Prefixes[ZCSPENDING_KEY] = {0xAB,0x36};
bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "zs";
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviews";
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivks";
bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-main";
bech32HRPs[SAPLING_EXTENDED_FVK] = "zxviews";
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
fMiningRequiresPeers = true; fMiningRequiresPeers = true;
@ -290,7 +313,7 @@ public:
consensus.fCoinbaseMustBeShielded = true; consensus.fCoinbaseMustBeShielded = true;
consensus.nSubsidySlowStartInterval = 20000; consensus.nSubsidySlowStartInterval = 20000;
consensus.nPreBlossomSubsidyHalvingInterval = Consensus::PRE_BLOSSOM_HALVING_INTERVAL; consensus.nPreBlossomSubsidyHalvingInterval = Consensus::PRE_BLOSSOM_HALVING_INTERVAL;
consensus.nPostBlossomSubsidyHalvingInterval = Consensus::POST_BLOSSOM_HALVING_INTERVAL; consensus.nPostBlossomSubsidyHalvingInterval = POST_BLOSSOM_HALVING_INTERVAL(Consensus::PRE_BLOSSOM_HALVING_INTERVAL);
consensus.nMajorityEnforceBlockUpgrade = 51; consensus.nMajorityEnforceBlockUpgrade = 51;
consensus.nMajorityRejectBlockOutdated = 75; consensus.nMajorityRejectBlockOutdated = 75;
consensus.nMajorityWindow = 400; consensus.nMajorityWindow = 400;
@ -332,6 +355,50 @@ public:
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight = consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight =
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
consensus.nFundingPeriodLength = consensus.nPostBlossomSubsidyHalvingInterval / 48;
// guarantees the first 2 characters, when base58 encoded, are "tm"
keyConstants.base58Prefixes[PUBKEY_ADDRESS] = {0x1D,0x25};
// guarantees the first 2 characters, when base58 encoded, are "t2"
keyConstants.base58Prefixes[SCRIPT_ADDRESS] = {0x1C,0xBA};
// the first character, when base58 encoded, is "9" or "c" (as in Bitcoin)
keyConstants.base58Prefixes[SECRET_KEY] = {0xEF};
// do not rely on these BIP32 prefixes; they are not specified and may change
keyConstants.base58Prefixes[EXT_PUBLIC_KEY] = {0x04,0x35,0x87,0xCF};
keyConstants.base58Prefixes[EXT_SECRET_KEY] = {0x04,0x35,0x83,0x94};
// guarantees the first 2 characters, when base58 encoded, are "zt"
keyConstants.base58Prefixes[ZCPAYMENT_ADDRESS] = {0x16,0xB6};
// guarantees the first 4 characters, when base58 encoded, are "ZiVt"
keyConstants.base58Prefixes[ZCVIEWING_KEY] = {0xA8,0xAC,0x0C};
// guarantees the first 2 characters, when base58 encoded, are "ST"
keyConstants.base58Prefixes[ZCSPENDING_KEY] = {0xAC,0x08};
keyConstants.bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "ztestsapling";
keyConstants.bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviewtestsapling";
keyConstants.bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivktestsapling";
keyConstants.bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-test";
keyConstants.bech32HRPs[SAPLING_EXTENDED_FVK] = "zxviewtestsapling";
// TODO: This `if` can be removed once canopy activation height is set.
if (consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight != Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT) {
std::vector<std::string> addresses(48, "");
consensus.AddZIP207FundingStream(
keyConstants,
Consensus::FS_ZIP214_ECC,
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight, 2726400,
addresses);
consensus.AddZIP207FundingStream(
keyConstants,
Consensus::FS_ZIP214_ZF,
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight, 2726400,
addresses);
consensus.AddZIP207FundingStream(
keyConstants,
Consensus::FS_ZIP214_MG,
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight, 2726400,
addresses);
}
// On testnet we activate this rule 6 blocks after Blossom activation. From block 299188 and // On testnet we activate this rule 6 blocks after Blossom activation. From block 299188 and
// prior to Blossom activation, the testnet minimum-difficulty threshold was 15 minutes (i.e. // prior to Blossom activation, the testnet minimum-difficulty threshold was 15 minutes (i.e.
// a minimum difficulty block can be mined if no block is mined normally within 15 minutes): // a minimum difficulty block can be mined if no block is mined normally within 15 minutes):
@ -375,28 +442,6 @@ public:
vSeeds.push_back(CDNSSeedData("zfnd.org", "testnet.seeder.zfnd.org")); // Zcash Foundation vSeeds.push_back(CDNSSeedData("zfnd.org", "testnet.seeder.zfnd.org")); // Zcash Foundation
vSeeds.push_back(CDNSSeedData("yolo.money", "testnet.is.yolo.money")); // gtank vSeeds.push_back(CDNSSeedData("yolo.money", "testnet.is.yolo.money")); // gtank
// guarantees the first 2 characters, when base58 encoded, are "tm"
base58Prefixes[PUBKEY_ADDRESS] = {0x1D,0x25};
// guarantees the first 2 characters, when base58 encoded, are "t2"
base58Prefixes[SCRIPT_ADDRESS] = {0x1C,0xBA};
// the first character, when base58 encoded, is "9" or "c" (as in Bitcoin)
base58Prefixes[SECRET_KEY] = {0xEF};
// do not rely on these BIP32 prefixes; they are not specified and may change
base58Prefixes[EXT_PUBLIC_KEY] = {0x04,0x35,0x87,0xCF};
base58Prefixes[EXT_SECRET_KEY] = {0x04,0x35,0x83,0x94};
// guarantees the first 2 characters, when base58 encoded, are "zt"
base58Prefixes[ZCPAYMENT_ADDRRESS] = {0x16,0xB6};
// guarantees the first 4 characters, when base58 encoded, are "ZiVt"
base58Prefixes[ZCVIEWING_KEY] = {0xA8,0xAC,0x0C};
// guarantees the first 2 characters, when base58 encoded, are "ST"
base58Prefixes[ZCSPENDING_KEY] = {0xAC,0x08};
bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "ztestsapling";
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviewtestsapling";
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivktestsapling";
bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-test";
bech32HRPs[SAPLING_EXTENDED_FVK] = "zxviewtestsapling";
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
fMiningRequiresPeers = true; fMiningRequiresPeers = true;
@ -456,7 +501,7 @@ public:
consensus.fCoinbaseMustBeShielded = false; consensus.fCoinbaseMustBeShielded = false;
consensus.nSubsidySlowStartInterval = 0; consensus.nSubsidySlowStartInterval = 0;
consensus.nPreBlossomSubsidyHalvingInterval = Consensus::PRE_BLOSSOM_REGTEST_HALVING_INTERVAL; consensus.nPreBlossomSubsidyHalvingInterval = Consensus::PRE_BLOSSOM_REGTEST_HALVING_INTERVAL;
consensus.nPostBlossomSubsidyHalvingInterval = Consensus::POST_BLOSSOM_REGTEST_HALVING_INTERVAL; consensus.nPostBlossomSubsidyHalvingInterval = POST_BLOSSOM_HALVING_INTERVAL(Consensus::PRE_BLOSSOM_REGTEST_HALVING_INTERVAL);
consensus.nMajorityEnforceBlockUpgrade = 750; consensus.nMajorityEnforceBlockUpgrade = 750;
consensus.nMajorityRejectBlockOutdated = 950; consensus.nMajorityRejectBlockOutdated = 950;
consensus.nMajorityWindow = 1000; consensus.nMajorityWindow = 1000;
@ -494,6 +539,26 @@ public:
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight = consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight =
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
consensus.nFundingPeriodLength = consensus.nPostBlossomSubsidyHalvingInterval / 48;
// Defined funding streams can be enabled with node config flags.
// These prefixes are the same as the testnet prefixes
keyConstants.base58Prefixes[PUBKEY_ADDRESS] = {0x1D,0x25};
keyConstants.base58Prefixes[SCRIPT_ADDRESS] = {0x1C,0xBA};
keyConstants.base58Prefixes[SECRET_KEY] = {0xEF};
// do not rely on these BIP32 prefixes; they are not specified and may change
keyConstants.base58Prefixes[EXT_PUBLIC_KEY] = {0x04,0x35,0x87,0xCF};
keyConstants.base58Prefixes[EXT_SECRET_KEY] = {0x04,0x35,0x83,0x94};
keyConstants.base58Prefixes[ZCPAYMENT_ADDRESS] = {0x16,0xB6};
keyConstants.base58Prefixes[ZCVIEWING_KEY] = {0xA8,0xAC,0x0C};
keyConstants.base58Prefixes[ZCSPENDING_KEY] = {0xAC,0x08};
keyConstants.bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "zregtestsapling";
keyConstants.bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviewregtestsapling";
keyConstants.bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivkregtestsapling";
keyConstants.bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-regtest";
keyConstants.bech32HRPs[SAPLING_EXTENDED_FVK] = "zxviewregtestsapling";
// The best chain should have at least this much work. // The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00"); consensus.nMinimumChainWork = uint256S("0x00");
@ -529,22 +594,6 @@ public:
0, 0,
0 0
}; };
// These prefixes are the same as the testnet prefixes
base58Prefixes[PUBKEY_ADDRESS] = {0x1D,0x25};
base58Prefixes[SCRIPT_ADDRESS] = {0x1C,0xBA};
base58Prefixes[SECRET_KEY] = {0xEF};
// do not rely on these BIP32 prefixes; they are not specified and may change
base58Prefixes[EXT_PUBLIC_KEY] = {0x04,0x35,0x87,0xCF};
base58Prefixes[EXT_SECRET_KEY] = {0x04,0x35,0x83,0x94};
base58Prefixes[ZCPAYMENT_ADDRRESS] = {0x16,0xB6};
base58Prefixes[ZCVIEWING_KEY] = {0xA8,0xAC,0x0C};
base58Prefixes[ZCSPENDING_KEY] = {0xAC,0x08};
bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "zregtestsapling";
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviewregtestsapling";
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivkregtestsapling";
bech32HRPs[SAPLING_EXTENDED_SPEND_KEY] = "secret-extended-key-regtest";
bech32HRPs[SAPLING_EXTENDED_FVK] = "zxviewregtestsapling";
// Founders reward script expects a vector of 2-of-3 multisig addresses // Founders reward script expects a vector of 2-of-3 multisig addresses
vFoundersRewardAddress = { "t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg" }; vFoundersRewardAddress = { "t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg" };
@ -557,6 +606,12 @@ public:
consensus.vUpgrades[idx].nActivationHeight = nActivationHeight; consensus.vUpgrades[idx].nActivationHeight = nActivationHeight;
} }
void UpdateFundingStreamParameters(Consensus::FundingStreamIndex idx, Consensus::FundingStream fs)
{
assert(idx >= Consensus::FIRST_FUNDING_STREAM && idx < Consensus::MAX_FUNDING_STREAMS);
consensus.vFundingStreams[idx] = fs;
}
void UpdateRegtestPow(int64_t nPowMaxAdjustDown, int64_t nPowMaxAdjustUp, uint256 powLimit) void UpdateRegtestPow(int64_t nPowMaxAdjustDown, int64_t nPowMaxAdjustUp, uint256 powLimit)
{ {
consensus.nPowMaxAdjustDown = nPowMaxAdjustDown; consensus.nPowMaxAdjustDown = nPowMaxAdjustDown;
@ -630,7 +685,8 @@ std::string CChainParams::GetFoundersRewardAddressAtHeight(int nHeight) const {
CScript CChainParams::GetFoundersRewardScriptAtHeight(int nHeight) const { CScript CChainParams::GetFoundersRewardScriptAtHeight(int nHeight) const {
assert(nHeight > 0 && nHeight <= consensus.GetLastFoundersRewardBlockHeight(nHeight)); assert(nHeight > 0 && nHeight <= consensus.GetLastFoundersRewardBlockHeight(nHeight));
CTxDestination address = DecodeDestination(GetFoundersRewardAddressAtHeight(nHeight).c_str()); KeyIO keyIO(*this);
CTxDestination address = keyIO.DecodeDestination(GetFoundersRewardAddressAtHeight(nHeight).c_str());
assert(IsValidDestination(address)); assert(IsValidDestination(address));
assert(IsScriptDestination(address)); assert(IsScriptDestination(address));
CScriptID scriptID = boost::get<CScriptID>(address); // address is a boost variant CScriptID scriptID = boost::get<CScriptID>(address); // address is a boost variant
@ -648,6 +704,11 @@ void UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex idx, int nActivation
regTestParams.UpdateNetworkUpgradeParameters(idx, nActivationHeight); regTestParams.UpdateNetworkUpgradeParameters(idx, nActivationHeight);
} }
void UpdateFundingStreamParameters(Consensus::FundingStreamIndex idx, Consensus::FundingStream fs)
{
regTestParams.UpdateFundingStreamParameters(idx, fs);
}
void UpdateRegtestPow(int64_t nPowMaxAdjustDown, int64_t nPowMaxAdjustUp, uint256 powLimit) { void UpdateRegtestPow(int64_t nPowMaxAdjustDown, int64_t nPowMaxAdjustUp, uint256 powLimit) {
regTestParams.UpdateRegtestPow(nPowMaxAdjustDown, nPowMaxAdjustUp, powLimit); regTestParams.UpdateRegtestPow(nPowMaxAdjustDown, nPowMaxAdjustUp, powLimit);
} }

View File

@ -32,6 +32,15 @@ struct CCheckpointData {
double fTransactionsPerDay; double fTransactionsPerDay;
}; };
class CBaseKeyConstants : public KeyConstants {
public:
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
const std::string& Bech32HRP(Bech32Type type) const { return bech32HRPs[type]; }
std::vector<unsigned char> base58Prefixes[KeyConstants::MAX_BASE58_TYPES];
std::string bech32HRPs[KeyConstants::MAX_BECH32_TYPES];
};
/** /**
* CChainParams defines various tweakable parameters of a given instance of the * CChainParams defines various tweakable parameters of a given instance of the
* Bitcoin system. There are three: the main network on which people trade goods * Bitcoin system. There are three: the main network on which people trade goods
@ -39,33 +48,9 @@ struct CCheckpointData {
* a regression test mode which is intended for private networks only. It has * a regression test mode which is intended for private networks only. It has
* minimal difficulty to ensure that blocks can be found instantly. * minimal difficulty to ensure that blocks can be found instantly.
*/ */
class CChainParams class CChainParams: public KeyConstants
{ {
public: public:
enum Base58Type {
PUBKEY_ADDRESS,
SCRIPT_ADDRESS,
SECRET_KEY,
EXT_PUBLIC_KEY,
EXT_SECRET_KEY,
ZCPAYMENT_ADDRRESS,
ZCSPENDING_KEY,
ZCVIEWING_KEY,
MAX_BASE58_TYPES
};
enum Bech32Type {
SAPLING_PAYMENT_ADDRESS,
SAPLING_FULL_VIEWING_KEY,
SAPLING_INCOMING_VIEWING_KEY,
SAPLING_EXTENDED_SPEND_KEY,
SAPLING_EXTENDED_FVK,
MAX_BECH32_TYPES
};
const Consensus::Params& GetConsensus() const { return consensus; } const Consensus::Params& GetConsensus() const { return consensus; }
const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; } const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; }
const std::vector<unsigned char>& AlertKey() const { return vAlertPubKey; } const std::vector<unsigned char>& AlertKey() const { return vAlertPubKey; }
@ -93,8 +78,12 @@ public:
/** Return the BIP70 network string (main, test or regtest) */ /** Return the BIP70 network string (main, test or regtest) */
std::string NetworkIDString() const { return strNetworkID; } std::string NetworkIDString() const { return strNetworkID; }
const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; } const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; }
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } const std::vector<unsigned char>& Base58Prefix(Base58Type type) const {
const std::string& Bech32HRP(Bech32Type type) const { return bech32HRPs[type]; } return keyConstants.Base58Prefix(type);
}
const std::string& Bech32HRP(Bech32Type type) const {
return keyConstants.Bech32HRP(type);
}
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; } const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; } const CCheckpointData& Checkpoints() const { return checkpointData; }
/** Return the founder's reward address and script for a given block height */ /** Return the founder's reward address and script for a given block height */
@ -113,8 +102,7 @@ protected:
int nDefaultPort = 0; int nDefaultPort = 0;
uint64_t nPruneAfterHeight = 0; uint64_t nPruneAfterHeight = 0;
std::vector<CDNSSeedData> vSeeds; std::vector<CDNSSeedData> vSeeds;
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES]; CBaseKeyConstants keyConstants;
std::string bech32HRPs[MAX_BECH32_TYPES];
std::string strNetworkID; std::string strNetworkID;
std::string strCurrencyUnits; std::string strCurrencyUnits;
uint32_t bip44CoinType; uint32_t bip44CoinType;
@ -158,4 +146,9 @@ void UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex idx, int nActivation
void UpdateRegtestPow(int64_t nPowMaxAdjustDown, int64_t nPowMaxAdjustUp, uint256 powLimit); void UpdateRegtestPow(int64_t nPowMaxAdjustDown, int64_t nPowMaxAdjustUp, uint256 powLimit);
/**
* Allows modifying the regtest funding stream parameters.
*/
void UpdateFundingStreamParameters(Consensus::FundingStreamIndex idx, Consensus::FundingStream fs);
#endif // BITCOIN_CHAINPARAMS_H #endif // BITCOIN_CHAINPARAMS_H

74
src/consensus/funding.cpp Normal file
View File

@ -0,0 +1,74 @@
// Copyright (c) 2020 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#include <consensus/funding.h>
namespace Consensus
{
/**
* General information about each funding stream.
* Ordered by Consensus::FundingStreamIndex.
*/
const struct FSInfo FundingStreamInfo[Consensus::MAX_FUNDING_STREAMS] = {
{
.recipient = "Electric Coin Company",
.specification = "https://zips.z.cash/zip-0214",
.valueNumerator = 7,
.valueDenominator = 100,
},
{
.recipient = "Zcash Foundation",
.specification = "https://zips.z.cash/zip-0214",
.valueNumerator = 5,
.valueDenominator = 100,
},
{
.recipient = "Major Grants",
.specification = "https://zips.z.cash/zip-0214",
.valueNumerator = 8,
.valueDenominator = 100,
}
};
CAmount FSInfo::Value(CAmount blockSubsidy) const
{
// Integer division is floor division for nonnegative integers in C++
return CAmount((blockSubsidy * valueNumerator) / valueDenominator);
}
std::set<FundingStreamElement> GetActiveFundingStreamElements(
int nHeight,
CAmount blockSubsidy,
const Consensus::Params& params)
{
std::set<std::pair<FundingStreamAddress, CAmount>> requiredElements;
for (uint32_t idx = Consensus::FIRST_FUNDING_STREAM; idx < Consensus::MAX_FUNDING_STREAMS; idx++) {
// The following indexed access is safe as Consensus::MAX_FUNDING_STREAMS is used
// in the definition of vFundingStreams.
auto fs = params.vFundingStreams[idx];
// Funding period is [startHeight, endHeight)
if (fs && nHeight >= fs.get().GetStartHeight() && nHeight < fs.get().GetEndHeight()) {
requiredElements.insert(std::make_pair(
fs.get().RecipientAddress(params, nHeight),
FundingStreamInfo[idx].Value(blockSubsidy)));
}
}
return requiredElements;
};
std::vector<FSInfo> GetActiveFundingStreams(
int nHeight,
const Consensus::Params& params)
{
std::vector<FSInfo> activeStreams;
for (uint32_t idx = Consensus::FIRST_FUNDING_STREAM; idx < Consensus::MAX_FUNDING_STREAMS; idx++) {
auto fs = params.vFundingStreams[idx];
if (fs && nHeight >= fs.get().GetStartHeight() && nHeight < fs.get().GetEndHeight()) {
activeStreams.push_back(FundingStreamInfo[idx]);
}
}
return activeStreams;
};
} // namespace Consensus

40
src/consensus/funding.h Normal file
View File

@ -0,0 +1,40 @@
// Copyright (c) 2020 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#ifndef ZCASH_CONSENSUS_FUNDING_H
#define ZCASH_CONSENSUS_FUNDING_H
#include <amount.h>
#include <consensus/params.h>
#include <boost/variant.hpp>
namespace Consensus
{
struct FSInfo {
std::string recipient;
std::string specification;
uint64_t valueNumerator;
uint64_t valueDenominator;
CAmount Value(CAmount blockSubsidy) const;
};
extern const struct FSInfo FundingStreamInfo[];
typedef std::pair<FundingStreamAddress, CAmount> FundingStreamElement;
std::set<FundingStreamElement> GetActiveFundingStreamElements(
int nHeight,
CAmount blockSubsidy,
const Consensus::Params& params);
std::vector<FSInfo> GetActiveFundingStreams(
int nHeight,
const Consensus::Params& params);
} // namespace Consensus
#endif // ZCASH_CONSENSUS_FUNDING_H

View File

@ -4,6 +4,9 @@
#include "params.h" #include "params.h"
#include <amount.h>
#include <key_io.h>
#include <script/standard.h>
#include "upgrades.h" #include "upgrades.h"
namespace Consensus { namespace Consensus {
@ -36,32 +39,153 @@ namespace Consensus {
} }
} }
int Params::GetLastFoundersRewardBlockHeight(int nHeight) const { /**
* This method determines the block height of the `halvingIndex`th
* halving, as known at the specified `nHeight` block height.
*
* Previous implementations of this logic were specialized to the
* first halving.
*/
int Params::HalvingHeight(int nHeight, int halvingIndex) const {
// zip208 // zip208
// FoundersRewardLastBlockHeight := max({ height ⦂ N | Halving(height) < 1 }) // HalvingHeight(i) := max({ height ⦂ N | Halving(height) < i }) + 1
// Halving(h) is defined as floor(f(h)) where f is a strictly increasing rational //
// function, so it's sufficient to solve for f(height) = 1 in the rationals and // Halving(h) returns the halving index at the specified height. It is
// then take ceiling(height - 1). // defined as floor(f(h)) where f is a strictly increasing rational
// H := blossom activation height; SS := SubsidySlowStartShift(); R := BLOSSOM_POW_TARGET_SPACING_RATIO // function, so it's sufficient to solve for f(height) = halvingIndex
// in the rationals and then take ceiling(height).
//
// H := blossom activation height;
// SS := SubsidySlowStartShift();
// R := 1 / (postInterval / preInterval) = BLOSSOM_POW_TARGET_SPACING_RATIO
// (The following calculation depends on BLOSSOM_POW_TARGET_SPACING_RATIO being an integer.)
//
// preBlossom: // preBlossom:
// 1 = (height - SS) / preInterval // i = (height - SS) / preInterval
// height = preInterval + SS // height = (preInterval * i) + SS
//
// postBlossom: // postBlossom:
// 1 = (H - SS) / preInterval + (height - H) / postInterval // i = (H - SS) / preInterval + (HalvingHeight(i) - H) / postInterval
// height = H + postInterval - (H - SS) * (postInterval / preInterval) // preInterval = postInterval / R
// height = H + postInterval - (H - SS) * R // i = (H - SS) / (postInterval / R) + (HalvingHeight(i) - H) / postInterval
// Note: This depends on R being an integer // i = (R * (H - SS) + HalvingHeight(i) - H) / postInterval
bool blossomActive = NetworkUpgradeActive(nHeight, Consensus::UPGRADE_BLOSSOM); // postInterval * i = R * (H - SS) + HalvingHeight(i) - H
if (blossomActive) { // HalvingHeight(i) = postInterval * i - R * (H - SS) + H
if (NetworkUpgradeActive(nHeight, Consensus::UPGRADE_BLOSSOM)) {
int blossomActivationHeight = vUpgrades[Consensus::UPGRADE_BLOSSOM].nActivationHeight; int blossomActivationHeight = vUpgrades[Consensus::UPGRADE_BLOSSOM].nActivationHeight;
// The following calculation depends on BLOSSOM_POW_TARGET_SPACING_RATIO being an integer.
return blossomActivationHeight + nPostBlossomSubsidyHalvingInterval return
- (blossomActivationHeight - SubsidySlowStartShift()) * BLOSSOM_POW_TARGET_SPACING_RATIO - 1; (nPostBlossomSubsidyHalvingInterval * halvingIndex)
- (BLOSSOM_POW_TARGET_SPACING_RATIO * (blossomActivationHeight - SubsidySlowStartShift()))
+ blossomActivationHeight;
} else { } else {
return nPreBlossomSubsidyHalvingInterval + SubsidySlowStartShift() - 1; return (nPreBlossomSubsidyHalvingInterval * halvingIndex) + SubsidySlowStartShift();
} }
} }
int Params::GetLastFoundersRewardBlockHeight(int nHeight) const {
return HalvingHeight(nHeight, 1) - 1;
}
int Params::FundingPeriodIndex(int fundingStreamStartHeight, int nHeight) const {
int firstHalvingHeight = HalvingHeight(fundingStreamStartHeight, 1);
// If the start height of the funding period is not aligned to a multiple of the
// funding period length, the first funding period will be shorter than the
// funding period length.
auto startPeriodOffset = (fundingStreamStartHeight - firstHalvingHeight) % nFundingPeriodLength;
if (startPeriodOffset < 0) startPeriodOffset += nFundingPeriodLength; // C++ '%' is remainder, not modulus!
return (nHeight - fundingStreamStartHeight + startPeriodOffset) / nFundingPeriodLength;
}
boost::variant<FundingStream, FundingStreamError> FundingStream::ValidateFundingStream(
const Consensus::Params& params,
const int startHeight,
const int endHeight,
const std::vector<FundingStreamAddress>& addresses
) {
if (!params.NetworkUpgradeActive(startHeight, Consensus::UPGRADE_CANOPY)) {
return FundingStreamError::CANOPY_NOT_ACTIVE;
}
if (endHeight < startHeight) {
return FundingStreamError::ILLEGAL_RANGE;
}
if (params.FundingPeriodIndex(startHeight, endHeight - 1) >= addresses.size()) {
return FundingStreamError::INSUFFICIENT_ADDRESSES;
}
return FundingStream(startHeight, endHeight, addresses);
};
class GetFundingStreamOrThrow: public boost::static_visitor<FundingStream> {
public:
FundingStream operator()(const FundingStream& fs) const {
return fs;
}
FundingStream operator()(const FundingStreamError& e) const {
switch (e) {
case FundingStreamError::CANOPY_NOT_ACTIVE:
throw std::runtime_error("Canopy network upgrade not active at funding stream start height.");
case FundingStreamError::ILLEGAL_RANGE:
throw std::runtime_error("Illegal start/end height combination for funding stream.");
case FundingStreamError::INSUFFICIENT_ADDRESSES:
throw std::runtime_error("Insufficient payment addresses to fully exhaust funding stream.");
default:
throw std::runtime_error("Unrecognized error validating funding stream.");
};
}
};
FundingStream FundingStream::ParseFundingStream(
const Consensus::Params& params,
const KeyConstants& keyConstants,
const int startHeight,
const int endHeight,
const std::vector<std::string>& strAddresses)
{
KeyIO keyIO(keyConstants);
// Parse the address strings into concrete types.
std::vector<FundingStreamAddress> addresses;
for (auto addr : strAddresses) {
auto taddr = keyIO.DecodeDestination(addr);
if (IsValidDestination(taddr)) {
addresses.push_back(GetScriptForDestination(taddr));
} else {
auto zaddr = keyIO.DecodePaymentAddress(addr);
// If the string is not a valid transparent or Sapling address, we will
// throw here.
addresses.push_back(boost::get<libzcash::SaplingPaymentAddress>(zaddr));
}
}
auto validationResult = FundingStream::ValidateFundingStream(params, startHeight, endHeight, addresses);
return boost::apply_visitor(GetFundingStreamOrThrow(), validationResult);
};
void Params::AddZIP207FundingStream(
const KeyConstants& keyConstants,
FundingStreamIndex idx,
int startHeight,
int endHeight,
const std::vector<std::string>& strAddresses)
{
vFundingStreams[idx] = FundingStream::ParseFundingStream(*this, keyConstants, startHeight, endHeight, strAddresses);
};
FundingStreamAddress FundingStream::RecipientAddress(const Consensus::Params& params, int nHeight) const
{
auto addressIndex = params.FundingPeriodIndex(startHeight, nHeight);
assert(addressIndex >= 0 && addressIndex < addresses.size());
return addresses[addressIndex];
};
int64_t Params::PoWTargetSpacing(int nHeight) const { int64_t Params::PoWTargetSpacing(int nHeight) const {
// zip208 // zip208
// PoWTargetSpacing(height) := // PoWTargetSpacing(height) :=

View File

@ -6,12 +6,18 @@
#ifndef BITCOIN_CONSENSUS_PARAMS_H #ifndef BITCOIN_CONSENSUS_PARAMS_H
#define BITCOIN_CONSENSUS_PARAMS_H #define BITCOIN_CONSENSUS_PARAMS_H
#include <script/script.h>
#include "uint256.h" #include "uint256.h"
#include "key_constants.h"
#include <zcash/address/sapling.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
namespace Consensus { namespace Consensus {
// Early declaration to ensure it is accessible.
struct Params;
/** /**
* Index into Params.vUpgrades and NetworkUpgradeInfo * Index into Params.vUpgrades and NetworkUpgradeInfo
* *
@ -75,16 +81,77 @@ struct NetworkUpgrade {
boost::optional<uint256> hashActivationBlock; boost::optional<uint256> hashActivationBlock;
}; };
typedef boost::variant<libzcash::SaplingPaymentAddress, CScript> FundingStreamAddress;
/**
* Index into Params.vFundingStreams.
*
* Being array indices, these MUST be numbered consecutively.
*/
enum FundingStreamIndex : uint32_t {
FS_ZIP214_ECC,
FS_ZIP214_ZF,
FS_ZIP214_MG,
MAX_FUNDING_STREAMS,
};
const auto FIRST_FUNDING_STREAM = FS_ZIP214_ECC;
enum FundingStreamError {
CANOPY_NOT_ACTIVE,
ILLEGAL_RANGE,
INSUFFICIENT_ADDRESSES,
};
class FundingStream
{
private:
int startHeight;
int endHeight;
std::vector<FundingStreamAddress> addresses;
FundingStream(int startHeight, int endHeight, const std::vector<FundingStreamAddress>& addresses):
startHeight(startHeight), endHeight(endHeight), addresses(addresses) { }
public:
FundingStream(const FundingStream& fs):
startHeight(fs.startHeight), endHeight(fs.endHeight), addresses(fs.addresses) { }
static boost::variant<FundingStream, FundingStreamError> ValidateFundingStream(
const Consensus::Params& params,
const int startHeight,
const int endHeight,
const std::vector<FundingStreamAddress>& addresses
);
static FundingStream ParseFundingStream(
const Consensus::Params& params,
const KeyConstants& keyConstants,
const int startHeight,
const int endHeight,
const std::vector<std::string>& strAddresses);
int GetStartHeight() const { return startHeight; };
int GetEndHeight() const { return endHeight; };
const std::vector<FundingStreamAddress>& GetAddresses() const {
return addresses;
};
FundingStreamAddress RecipientAddress(const Params& params, int nHeight) const;
};
/** ZIP208 block target interval in seconds. */ /** ZIP208 block target interval in seconds. */
static const unsigned int PRE_BLOSSOM_POW_TARGET_SPACING = 150; static const unsigned int PRE_BLOSSOM_POW_TARGET_SPACING = 150;
static const unsigned int POST_BLOSSOM_POW_TARGET_SPACING = 75; static const unsigned int POST_BLOSSOM_POW_TARGET_SPACING = 75;
static_assert(POST_BLOSSOM_POW_TARGET_SPACING < PRE_BLOSSOM_POW_TARGET_SPACING, "Blossom target spacing must be less than pre-Blossom target spacing."); static_assert(PRE_BLOSSOM_POW_TARGET_SPACING > POST_BLOSSOM_POW_TARGET_SPACING, "Blossom target spacing must be less than pre-Blossom target spacing.");
static const unsigned int PRE_BLOSSOM_HALVING_INTERVAL = 840000; static_assert(PRE_BLOSSOM_POW_TARGET_SPACING % POST_BLOSSOM_POW_TARGET_SPACING == 0, "Blossom target spacing must exactly divide pre-Blossom target spacing.");
static const unsigned int PRE_BLOSSOM_REGTEST_HALVING_INTERVAL = 150;
static const int BLOSSOM_POW_TARGET_SPACING_RATIO = PRE_BLOSSOM_POW_TARGET_SPACING / POST_BLOSSOM_POW_TARGET_SPACING; static const int BLOSSOM_POW_TARGET_SPACING_RATIO = PRE_BLOSSOM_POW_TARGET_SPACING / POST_BLOSSOM_POW_TARGET_SPACING;
static_assert(BLOSSOM_POW_TARGET_SPACING_RATIO * POST_BLOSSOM_POW_TARGET_SPACING == PRE_BLOSSOM_POW_TARGET_SPACING, "Invalid BLOSSOM_POW_TARGET_SPACING_RATIO"); static_assert(BLOSSOM_POW_TARGET_SPACING_RATIO * POST_BLOSSOM_POW_TARGET_SPACING == PRE_BLOSSOM_POW_TARGET_SPACING, "Invalid BLOSSOM_POW_TARGET_SPACING_RATIO");
static const unsigned int POST_BLOSSOM_HALVING_INTERVAL = PRE_BLOSSOM_HALVING_INTERVAL * BLOSSOM_POW_TARGET_SPACING_RATIO;
static const unsigned int POST_BLOSSOM_REGTEST_HALVING_INTERVAL = PRE_BLOSSOM_REGTEST_HALVING_INTERVAL * BLOSSOM_POW_TARGET_SPACING_RATIO; static const unsigned int PRE_BLOSSOM_HALVING_INTERVAL = 840000;
static const unsigned int PRE_BLOSSOM_REGTEST_HALVING_INTERVAL = 144;
#define POST_BLOSSOM_HALVING_INTERVAL(preBlossomInterval) \
(preBlossomInterval * Consensus::BLOSSOM_POW_TARGET_SPACING_RATIO)
/** /**
* Parameters that influence chain consensus. * Parameters that influence chain consensus.
@ -120,16 +187,36 @@ struct Params {
int nPreBlossomSubsidyHalvingInterval; int nPreBlossomSubsidyHalvingInterval;
int nPostBlossomSubsidyHalvingInterval; int nPostBlossomSubsidyHalvingInterval;
/**
* Identify the halving index at the specified height. The result will be
* negative during the slow-start period.
*/
int Halving(int nHeight) const; int Halving(int nHeight) const;
/**
* Get the block height of the specified halving.
*/
int HalvingHeight(int nHeight, int halvingIndex) const;
int GetLastFoundersRewardBlockHeight(int nHeight) const; int GetLastFoundersRewardBlockHeight(int nHeight) const;
int FundingPeriodIndex(int fundingStreamStartHeight, int nHeight) const;
/** Used to check majorities for block version upgrade */ /** Used to check majorities for block version upgrade */
int nMajorityEnforceBlockUpgrade; int nMajorityEnforceBlockUpgrade;
int nMajorityRejectBlockOutdated; int nMajorityRejectBlockOutdated;
int nMajorityWindow; int nMajorityWindow;
NetworkUpgrade vUpgrades[MAX_NETWORK_UPGRADES]; NetworkUpgrade vUpgrades[MAX_NETWORK_UPGRADES];
int nFundingPeriodLength;
boost::optional<FundingStream> vFundingStreams[MAX_FUNDING_STREAMS];
void AddZIP207FundingStream(
const KeyConstants& keyConstants,
FundingStreamIndex idx,
int startHeight,
int endHeight,
const std::vector<std::string>& addresses);
/** /**
* Default block height at which the future timestamp soft fork rule activates. * Default block height at which the future timestamp soft fork rule activates.
* *
@ -188,6 +275,7 @@ struct Params {
uint256 nMinimumChainWork; uint256 nMinimumChainWork;
}; };
} // namespace Consensus } // namespace Consensus
#endif // BITCOIN_CONSENSUS_PARAMS_H #endif // BITCOIN_CONSENSUS_PARAMS_H

View File

@ -142,9 +142,10 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey,
out.pushKV("reqSigs", nRequired); out.pushKV("reqSigs", nRequired);
out.pushKV("type", GetTxnOutputType(type)); out.pushKV("type", GetTxnOutputType(type));
KeyIO keyIO(Params());
UniValue a(UniValue::VARR); UniValue a(UniValue::VARR);
for (const CTxDestination& addr : addresses) { for (const CTxDestination& addr : addresses) {
a.push_back(EncodeDestination(addr)); a.push_back(keyIO.EncodeDestination(addr));
} }
out.pushKV("addresses", a); out.pushKV("addresses", a);
} }

View File

@ -96,9 +96,10 @@ protected:
mtx.vout[0].nValue = 0; mtx.vout[0].nValue = 0;
// Give it a Founder's Reward vout for height 1. // Give it a Founder's Reward vout for height 1.
auto rewardScript = Params().GetFoundersRewardScriptAtHeight(1);
mtx.vout.push_back(CTxOut( mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5, GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1))); rewardScript));
return mtx; return mtx;
} }

View File

@ -3,6 +3,8 @@
#include "main.h" #include "main.h"
#include "utilmoneystr.h" #include "utilmoneystr.h"
#include "chainparams.h" #include "chainparams.h"
#include "consensus/funding.h"
#include "key_io.h"
#include "utilstrencodings.h" #include "utilstrencodings.h"
#include "zcash/Address.hpp" #include "zcash/Address.hpp"
#include "wallet/wallet.h" #include "wallet/wallet.h"
@ -40,6 +42,7 @@ TEST(FoundersRewardTest, create_testnet_2of3multisig) {
pubkeys.resize(3); pubkeys.resize(3);
CPubKey newKey; CPubKey newKey;
std::vector<std::string> addresses; std::vector<std::string> addresses;
KeyIO keyIO(Params());
for (int i = 0; i < numKeys; i++) { for (int i = 0; i < numKeys; i++) {
ASSERT_TRUE(pWallet->GetKeyFromPool(newKey)); ASSERT_TRUE(pWallet->GetKeyFromPool(newKey));
pubkeys[0] = newKey; pubkeys[0] = newKey;
@ -59,7 +62,7 @@ TEST(FoundersRewardTest, create_testnet_2of3multisig) {
pWallet->AddCScript(result); pWallet->AddCScript(result);
pWallet->SetAddressBook(innerID, "", "receive"); pWallet->SetAddressBook(innerID, "", "receive");
std::string address = EncodeDestination(innerID); std::string address = keyIO.EncodeDestination(innerID);
addresses.push_back(address); addresses.push_back(address);
} }
@ -99,6 +102,17 @@ void checkNumberOfUniqueAddresses(int nUnique) {
EXPECT_EQ(addresses.size(), nUnique); EXPECT_EQ(addresses.size(), nUnique);
} }
int GetMaxFundingStreamHeight(const Consensus::Params& params) {
int result = 0;
for (auto fs : params.vFundingStreams) {
if (fs && result < fs.get().GetEndHeight() - 1) {
result = fs.get().GetEndHeight() - 1;
}
}
return result;
}
TEST(FoundersRewardTest, General) { TEST(FoundersRewardTest, General) {
SelectParams(CBaseChainParams::TESTNET); SelectParams(CBaseChainParams::TESTNET);
@ -125,7 +139,7 @@ TEST(FoundersRewardTest, General) {
EXPECT_DEATH(params.GetFoundersRewardAddressAtHeight(maxHeight+1), "nHeight"); EXPECT_DEATH(params.GetFoundersRewardAddressAtHeight(maxHeight+1), "nHeight");
} }
TEST(founders_reward_test, regtest_get_last_block_blossom) { TEST(FoundersRewardTest, RegtestGetLastBlockBlossom) {
int blossomActivationHeight = Consensus::PRE_BLOSSOM_REGTEST_HALVING_INTERVAL / 2; // = 75 int blossomActivationHeight = Consensus::PRE_BLOSSOM_REGTEST_HALVING_INTERVAL / 2; // = 75
auto params = RegtestActivateBlossom(false, blossomActivationHeight); auto params = RegtestActivateBlossom(false, blossomActivationHeight);
int lastFRHeight = params.GetLastFoundersRewardBlockHeight(blossomActivationHeight); int lastFRHeight = params.GetLastFoundersRewardBlockHeight(blossomActivationHeight);
@ -134,7 +148,7 @@ TEST(founders_reward_test, regtest_get_last_block_blossom) {
RegtestDeactivateBlossom(); RegtestDeactivateBlossom();
} }
TEST(founders_reward_test, mainnet_get_last_block) { TEST(FoundersRewardTest, MainnetGetLastBlock) {
SelectParams(CBaseChainParams::MAIN); SelectParams(CBaseChainParams::MAIN);
auto params = Params().GetConsensus(); auto params = Params().GetConsensus();
int lastFRHeight = GetLastFoundersRewardHeight(params); int lastFRHeight = GetLastFoundersRewardHeight(params);
@ -216,3 +230,71 @@ TEST(FoundersRewardTest, PerAddressRewardTestnet) {
SelectParams(CBaseChainParams::TESTNET); SelectParams(CBaseChainParams::TESTNET);
verifyNumberOfRewards(); verifyNumberOfRewards();
} }
// Verify that post-Canopy, block rewards are split according to ZIP 207.
TEST(FundingStreamsRewardTest, Zip207Distribution) {
auto consensus = RegtestActivateCanopy(false, 200);
int minHeight = GetLastFoundersRewardHeight(consensus) + 1;
KeyIO keyIO(Params());
auto sk = libzcash::SaplingSpendingKey(uint256());
for (int idx = Consensus::FIRST_FUNDING_STREAM; idx < Consensus::MAX_FUNDING_STREAMS; idx++) {
// we can just use the same addresses for all streams, all we're trying to do here
// is validate that the streams add up to the 20% of block reward.
auto shieldedAddr = keyIO.EncodePaymentAddress(sk.default_address());
UpdateFundingStreamParameters(
(Consensus::FundingStreamIndex) idx,
Consensus::FundingStream::ParseFundingStream(
consensus,
Params(),
minHeight,
minHeight + 12,
{
"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi",
shieldedAddr,
}
)
);
}
int maxHeight = GetMaxFundingStreamHeight(consensus);
std::map<std::string, CAmount> ms;
for (int nHeight = minHeight; nHeight <= maxHeight; nHeight++) {
auto blockSubsidy = GetBlockSubsidy(nHeight, consensus);
auto elems = GetActiveFundingStreamElements(nHeight, blockSubsidy, consensus);
CAmount totalFunding = 0;
for (Consensus::FundingStreamElement elem : elems) {
totalFunding += elem.second;
}
EXPECT_EQ(totalFunding, blockSubsidy / 5);
}
RegtestDeactivateCanopy();
}
TEST(FundingStreamsRewardTest, ParseFundingStream) {
auto consensus = RegtestActivateCanopy(false, 200);
int minHeight = GetLastFoundersRewardHeight(consensus) + 1;
KeyIO keyIO(Params());
auto sk = libzcash::SaplingSpendingKey(uint256());
auto shieldedAddr = keyIO.EncodePaymentAddress(sk.default_address());
ASSERT_THROW(
Consensus::FundingStream::ParseFundingStream(
consensus,
Params(),
minHeight,
minHeight + 13,
{
"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi",
shieldedAddr,
}
),
std::runtime_error
);
RegtestDeactivateCanopy();
}

View File

@ -9,18 +9,19 @@
TEST(Keys, EncodeAndDecodeSapling) TEST(Keys, EncodeAndDecodeSapling)
{ {
SelectParams(CBaseChainParams::MAIN); SelectParams(CBaseChainParams::MAIN);
KeyIO keyIO(Params());
auto m = GetTestMasterSaplingSpendingKey(); auto m = GetTestMasterSaplingSpendingKey();
for (uint32_t i = 0; i < 1000; i++) { for (uint32_t i = 0; i < 1000; i++) {
auto sk = m.Derive(i); auto sk = m.Derive(i);
{ {
std::string sk_string = EncodeSpendingKey(sk); std::string sk_string = keyIO.EncodeSpendingKey(sk);
EXPECT_EQ( EXPECT_EQ(
sk_string.substr(0, 24), sk_string.substr(0, 24),
Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY)); Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY));
auto spendingkey2 = DecodeSpendingKey(sk_string); auto spendingkey2 = keyIO.DecodeSpendingKey(sk_string);
EXPECT_TRUE(IsValidSpendingKey(spendingkey2)); EXPECT_TRUE(IsValidSpendingKey(spendingkey2));
ASSERT_TRUE(boost::get<libzcash::SaplingExtendedSpendingKey>(&spendingkey2) != nullptr); ASSERT_TRUE(boost::get<libzcash::SaplingExtendedSpendingKey>(&spendingkey2) != nullptr);
@ -29,12 +30,12 @@ TEST(Keys, EncodeAndDecodeSapling)
} }
{ {
auto extfvk = sk.ToXFVK(); auto extfvk = sk.ToXFVK();
std::string vk_string = EncodeViewingKey(extfvk); std::string vk_string = keyIO.EncodeViewingKey(extfvk);
EXPECT_EQ( EXPECT_EQ(
vk_string.substr(0, 7), vk_string.substr(0, 7),
Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_FVK)); Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_FVK));
auto viewingkey2 = DecodeViewingKey(vk_string); auto viewingkey2 = keyIO.DecodeViewingKey(vk_string);
EXPECT_TRUE(IsValidViewingKey(viewingkey2)); EXPECT_TRUE(IsValidViewingKey(viewingkey2));
ASSERT_TRUE(boost::get<libzcash::SaplingExtendedFullViewingKey>(&viewingkey2) != nullptr); ASSERT_TRUE(boost::get<libzcash::SaplingExtendedFullViewingKey>(&viewingkey2) != nullptr);
@ -44,12 +45,12 @@ TEST(Keys, EncodeAndDecodeSapling)
{ {
auto addr = sk.DefaultAddress(); auto addr = sk.DefaultAddress();
std::string addr_string = EncodePaymentAddress(addr); std::string addr_string = keyIO.EncodePaymentAddress(addr);
EXPECT_EQ( EXPECT_EQ(
addr_string.substr(0, 2), addr_string.substr(0, 2),
Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS)); Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS));
auto paymentaddr2 = DecodePaymentAddress(addr_string); auto paymentaddr2 = keyIO.DecodePaymentAddress(addr_string);
EXPECT_TRUE(IsValidPaymentAddress(paymentaddr2)); EXPECT_TRUE(IsValidPaymentAddress(paymentaddr2));
ASSERT_TRUE(boost::get<libzcash::SaplingPaymentAddress>(&paymentaddr2) != nullptr); ASSERT_TRUE(boost::get<libzcash::SaplingPaymentAddress>(&paymentaddr2) != nullptr);

View File

@ -439,6 +439,9 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT)); strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT));
strUsage += HelpMessageOpt("-nuparams=hexBranchId:activationHeight", "Use given activation height for specified network upgrade (regtest-only)"); strUsage += HelpMessageOpt("-nuparams=hexBranchId:activationHeight", "Use given activation height for specified network upgrade (regtest-only)");
strUsage += HelpMessageOpt("-nurejectoldversions", strprintf("Reject peers that don't know about the current epoch (regtest-only) (default: %u)", DEFAULT_NU_REJECT_OLD_VERSIONS)); strUsage += HelpMessageOpt("-nurejectoldversions", strprintf("Reject peers that don't know about the current epoch (regtest-only) (default: %u)", DEFAULT_NU_REJECT_OLD_VERSIONS));
strUsage += HelpMessageOpt(
"-fundingstream=streamId:startHeight:endHeight:comma_delimited_addresses",
"Use given addresses for block subsidy share paid to the funding stream with id <streamId> (regtest-only)");
} }
string debugCategories = "addrman, alert, bench, coindb, db, estimatefee, http, libevent, lock, mempool, net, partitioncheck, pow, proxy, prune, " string debugCategories = "addrman, alert, bench, coindb, db, estimatefee, http, libevent, lock, mempool, net, partitioncheck, pow, proxy, prune, "
"rand, reindex, rpc, selectcoins, tor, zmq, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these "rand, reindex, rpc, selectcoins, tor, zmq, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these
@ -1036,12 +1039,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE); nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
KeyIO keyIO(chainparams);
#ifdef ENABLE_MINING #ifdef ENABLE_MINING
if (mapArgs.count("-mineraddress")) { if (mapArgs.count("-mineraddress")) {
CTxDestination addr = DecodeDestination(mapArgs["-mineraddress"]); CTxDestination addr = keyIO.DecodeDestination(mapArgs["-mineraddress"]);
if (!IsValidDestination(addr)) { if (!IsValidDestination(addr)) {
// Try a Sapling address // Try a Sapling address
auto zaddr = DecodePaymentAddress(mapArgs["-mineraddress"]); auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]);
if (!IsValidPaymentAddress(zaddr) || if (!IsValidPaymentAddress(zaddr) ||
boost::get<libzcash::SaplingPaymentAddress>(&zaddr) == nullptr) boost::get<libzcash::SaplingPaymentAddress>(&zaddr) == nullptr)
{ {
@ -1092,6 +1096,45 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
} }
} }
if (!mapMultiArgs["-fundingstream"].empty()) {
// Allow overriding network upgrade parameters for testing
if (Params().NetworkIDString() != "regtest") {
return InitError("Funding stream parameters may only be overridden on regtest.");
}
const std::vector<std::string>& streams = mapMultiArgs["-fundingstream"];
for (auto i : streams) {
std::vector<std::string> vStreamParams;
boost::split(vStreamParams, i, boost::is_any_of(":"));
if (vStreamParams.size() != 4) {
return InitError("Funding stream parameters malformed, expecting streamId:startHeight:endHeight:comma_delimited_addresses");
}
int nFundingStreamId;
if (!ParseInt32(vStreamParams[0], &nFundingStreamId) ||
nFundingStreamId < Consensus::FIRST_FUNDING_STREAM ||
nFundingStreamId >= Consensus::MAX_FUNDING_STREAMS) {
return InitError(strprintf("Invalid streamId (%s)", vStreamParams[0]));
}
int nStartHeight;
if (!ParseInt32(vStreamParams[1], &nStartHeight)) {
return InitError(strprintf("Invalid funding stream start height (%s)", vStreamParams[1]));
}
int nEndHeight;
if (!ParseInt32(vStreamParams[2], &nEndHeight)) {
return InitError(strprintf("Invalid funding stream end height (%s)", vStreamParams[2]));
}
std::vector<std::string> vStreamAddrs;
boost::split(vStreamAddrs, vStreamParams[3], boost::is_any_of(","));
auto fs = Consensus::FundingStream::ParseFundingStream(
Params().GetConsensus(), Params(), nStartHeight, nEndHeight, vStreamAddrs);
UpdateFundingStreamParameters((Consensus::FundingStreamIndex) nFundingStreamId, fs);
}
}
// ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log
// Initialize libsodium // Initialize libsodium
@ -1545,12 +1588,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
bool minerAddressInLocalWallet = false; bool minerAddressInLocalWallet = false;
if (pwalletMain) { if (pwalletMain) {
CTxDestination addr = DecodeDestination(mapArgs["-mineraddress"]); CTxDestination addr = keyIO.DecodeDestination(mapArgs["-mineraddress"]);
if (IsValidDestination(addr)) { if (IsValidDestination(addr)) {
CKeyID keyID = boost::get<CKeyID>(addr); CKeyID keyID = boost::get<CKeyID>(addr);
minerAddressInLocalWallet = pwalletMain->HaveKey(keyID); minerAddressInLocalWallet = pwalletMain->HaveKey(keyID);
} else { } else {
auto zaddr = DecodePaymentAddress(mapArgs["-mineraddress"]); auto zaddr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]);
minerAddressInLocalWallet = boost::apply_visitor( minerAddressInLocalWallet = boost::apply_visitor(
HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr); HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr);
} }

39
src/key_constants.h Normal file
View File

@ -0,0 +1,39 @@
// Copyright (c) 2020 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#ifndef ZCASH_KEY_CONSTANTS_H
#define ZCASH_KEY_CONSTANTS_H
class KeyConstants
{
public:
enum Base58Type {
PUBKEY_ADDRESS,
SCRIPT_ADDRESS,
SECRET_KEY,
EXT_PUBLIC_KEY,
EXT_SECRET_KEY,
ZCPAYMENT_ADDRESS,
ZCSPENDING_KEY,
ZCVIEWING_KEY,
MAX_BASE58_TYPES
};
enum Bech32Type {
SAPLING_PAYMENT_ADDRESS,
SAPLING_FULL_VIEWING_KEY,
SAPLING_INCOMING_VIEWING_KEY,
SAPLING_EXTENDED_SPEND_KEY,
SAPLING_EXTENDED_FVK,
MAX_BECH32_TYPES
};
virtual const std::vector<unsigned char>& Base58Prefix(Base58Type type) const =0;
virtual const std::string& Bech32HRP(Bech32Type type) const =0;
};
#endif // ZCASH_KEY_CONSTANTS_H

View File

@ -22,21 +22,21 @@ namespace
class DestinationEncoder : public boost::static_visitor<std::string> class DestinationEncoder : public boost::static_visitor<std::string>
{ {
private: private:
const CChainParams& m_params; const KeyConstants& keyConstants;
public: public:
DestinationEncoder(const CChainParams& params) : m_params(params) {} DestinationEncoder(const KeyConstants& keyConstants) : keyConstants(keyConstants) {}
std::string operator()(const CKeyID& id) const std::string operator()(const CKeyID& id) const
{ {
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::PUBKEY_ADDRESS);
data.insert(data.end(), id.begin(), id.end()); data.insert(data.end(), id.begin(), id.end());
return EncodeBase58Check(data); return EncodeBase58Check(data);
} }
std::string operator()(const CScriptID& id) const std::string operator()(const CScriptID& id) const
{ {
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::SCRIPT_ADDRESS);
data.insert(data.end(), id.begin(), id.end()); data.insert(data.end(), id.begin(), id.end());
return EncodeBase58Check(data); return EncodeBase58Check(data);
} }
@ -44,43 +44,19 @@ public:
std::string operator()(const CNoDestination& no) const { return {}; } std::string operator()(const CNoDestination& no) const { return {}; }
}; };
CTxDestination DecodeDestination(const std::string& str, const CChainParams& params)
{
std::vector<unsigned char> data;
uint160 hash;
if (DecodeBase58Check(str, data)) {
// 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.
const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) {
std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin());
return CKeyID(hash);
}
// Script-hash-addresses have version 5 (or 196 testnet).
// The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) {
std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin());
return CScriptID(hash);
}
}
return CNoDestination();
}
class PaymentAddressEncoder : public boost::static_visitor<std::string> class PaymentAddressEncoder : public boost::static_visitor<std::string>
{ {
private: private:
const CChainParams& m_params; const KeyConstants& keyConstants;
public: public:
PaymentAddressEncoder(const CChainParams& params) : m_params(params) {} PaymentAddressEncoder(const KeyConstants& keyConstants) : keyConstants(keyConstants) {}
std::string operator()(const libzcash::SproutPaymentAddress& zaddr) const std::string operator()(const libzcash::SproutPaymentAddress& zaddr) const
{ {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << zaddr; ss << zaddr;
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS); std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::ZCPAYMENT_ADDRESS);
data.insert(data.end(), ss.begin(), ss.end()); data.insert(data.end(), ss.begin(), ss.end());
return EncodeBase58Check(data); return EncodeBase58Check(data);
} }
@ -95,7 +71,7 @@ public:
// See calculation comment below // See calculation comment below
data.reserve((seraddr.size() * 8 + 4) / 5); data.reserve((seraddr.size() * 8 + 4) / 5);
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, seraddr.begin(), seraddr.end()); ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, seraddr.begin(), seraddr.end());
return bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS), data); return bech32::Encode(keyConstants.Bech32HRP(KeyConstants::SAPLING_PAYMENT_ADDRESS), data);
} }
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; } std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
@ -104,16 +80,16 @@ public:
class ViewingKeyEncoder : public boost::static_visitor<std::string> class ViewingKeyEncoder : public boost::static_visitor<std::string>
{ {
private: private:
const CChainParams& m_params; const KeyConstants& keyConstants;
public: public:
ViewingKeyEncoder(const CChainParams& params) : m_params(params) {} ViewingKeyEncoder(const KeyConstants& keyConstants) : keyConstants(keyConstants) {}
std::string operator()(const libzcash::SproutViewingKey& vk) const std::string operator()(const libzcash::SproutViewingKey& vk) const
{ {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << vk; ss << vk;
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCVIEWING_KEY); std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::ZCVIEWING_KEY);
data.insert(data.end(), ss.begin(), ss.end()); data.insert(data.end(), ss.begin(), ss.end());
std::string ret = EncodeBase58Check(data); std::string ret = EncodeBase58Check(data);
memory_cleanse(data.data(), data.size()); memory_cleanse(data.data(), data.size());
@ -130,7 +106,7 @@ public:
// See calculation comment below // See calculation comment below
data.reserve((serkey.size() * 8 + 4) / 5); data.reserve((serkey.size() * 8 + 4) / 5);
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, serkey.begin(), serkey.end()); ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, serkey.begin(), serkey.end());
std::string ret = bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_EXTENDED_FVK), data); std::string ret = bech32::Encode(keyConstants.Bech32HRP(KeyConstants::SAPLING_EXTENDED_FVK), data);
memory_cleanse(serkey.data(), serkey.size()); memory_cleanse(serkey.data(), serkey.size());
memory_cleanse(data.data(), data.size()); memory_cleanse(data.data(), data.size());
return ret; return ret;
@ -142,16 +118,16 @@ public:
class SpendingKeyEncoder : public boost::static_visitor<std::string> class SpendingKeyEncoder : public boost::static_visitor<std::string>
{ {
private: private:
const CChainParams& m_params; const KeyConstants& keyConstants;
public: public:
SpendingKeyEncoder(const CChainParams& params) : m_params(params) {} SpendingKeyEncoder(const KeyConstants& keyConstants) : keyConstants(keyConstants) {}
std::string operator()(const libzcash::SproutSpendingKey& zkey) const std::string operator()(const libzcash::SproutSpendingKey& zkey) const
{ {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << zkey; ss << zkey;
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCSPENDING_KEY); std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::ZCSPENDING_KEY);
data.insert(data.end(), ss.begin(), ss.end()); data.insert(data.end(), ss.begin(), ss.end());
std::string ret = EncodeBase58Check(data); std::string ret = EncodeBase58Check(data);
memory_cleanse(data.data(), data.size()); memory_cleanse(data.data(), data.size());
@ -168,7 +144,7 @@ public:
// See calculation comment below // See calculation comment below
data.reserve((serkey.size() * 8 + 4) / 5); data.reserve((serkey.size() * 8 + 4) / 5);
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, serkey.begin(), serkey.end()); ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, serkey.begin(), serkey.end());
std::string ret = bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY), data); std::string ret = bech32::Encode(keyConstants.Bech32HRP(KeyConstants::SAPLING_EXTENDED_SPEND_KEY), data);
memory_cleanse(serkey.data(), serkey.size()); memory_cleanse(serkey.data(), serkey.size());
memory_cleanse(data.data(), data.size()); memory_cleanse(data.data(), data.size());
return ret; return ret;
@ -186,12 +162,36 @@ const size_t ConvertedSaplingExtendedFullViewingKeySize = (ZIP32_XFVK_SIZE * 8 +
const size_t ConvertedSaplingExtendedSpendingKeySize = (ZIP32_XSK_SIZE * 8 + 4) / 5; const size_t ConvertedSaplingExtendedSpendingKeySize = (ZIP32_XSK_SIZE * 8 + 4) / 5;
} // namespace } // namespace
CKey DecodeSecret(const std::string& str) CTxDestination KeyIO::DecodeDestination(const std::string& str)
{
std::vector<unsigned char> data;
uint160 hash;
if (DecodeBase58Check(str, data)) {
// 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.
const std::vector<unsigned char>& pubkey_prefix = keyConstants.Base58Prefix(KeyConstants::PUBKEY_ADDRESS);
if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) {
std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin());
return CKeyID(hash);
}
// Script-hash-addresses have version 5 (or 196 testnet).
// The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
const std::vector<unsigned char>& script_prefix = keyConstants.Base58Prefix(KeyConstants::SCRIPT_ADDRESS);
if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) {
std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin());
return CScriptID(hash);
}
}
return CNoDestination();
};
CKey KeyIO::DecodeSecret(const std::string& str)
{ {
CKey key; CKey key;
std::vector<unsigned char> data; std::vector<unsigned char> data;
if (DecodeBase58Check(str, data)) { if (DecodeBase58Check(str, data)) {
const std::vector<unsigned char>& privkey_prefix = Params().Base58Prefix(CChainParams::SECRET_KEY); const std::vector<unsigned char>& privkey_prefix = keyConstants.Base58Prefix(KeyConstants::SECRET_KEY);
if ((data.size() == 32 + privkey_prefix.size() || (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) && if ((data.size() == 32 + privkey_prefix.size() || (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) &&
std::equal(privkey_prefix.begin(), privkey_prefix.end(), data.begin())) { std::equal(privkey_prefix.begin(), privkey_prefix.end(), data.begin())) {
bool compressed = data.size() == 33 + privkey_prefix.size(); bool compressed = data.size() == 33 + privkey_prefix.size();
@ -202,10 +202,10 @@ CKey DecodeSecret(const std::string& str)
return key; return key;
} }
std::string EncodeSecret(const CKey& key) std::string KeyIO::EncodeSecret(const CKey& key)
{ {
assert(key.IsValid()); assert(key.IsValid());
std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::SECRET_KEY); std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::SECRET_KEY);
data.insert(data.end(), key.begin(), key.end()); data.insert(data.end(), key.begin(), key.end());
if (key.IsCompressed()) { if (key.IsCompressed()) {
data.push_back(1); data.push_back(1);
@ -215,12 +215,12 @@ std::string EncodeSecret(const CKey& key)
return ret; return ret;
} }
CExtPubKey DecodeExtPubKey(const std::string& str) CExtPubKey KeyIO::DecodeExtPubKey(const std::string& str)
{ {
CExtPubKey key; CExtPubKey key;
std::vector<unsigned char> data; std::vector<unsigned char> data;
if (DecodeBase58Check(str, data)) { if (DecodeBase58Check(str, data)) {
const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY); const std::vector<unsigned char>& prefix = keyConstants.Base58Prefix(KeyConstants::EXT_PUBLIC_KEY);
if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) { if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) {
key.Decode(data.data() + prefix.size()); key.Decode(data.data() + prefix.size());
} }
@ -228,9 +228,9 @@ CExtPubKey DecodeExtPubKey(const std::string& str)
return key; return key;
} }
std::string EncodeExtPubKey(const CExtPubKey& key) std::string KeyIO::EncodeExtPubKey(const CExtPubKey& key)
{ {
std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY); std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::EXT_PUBLIC_KEY);
size_t size = data.size(); size_t size = data.size();
data.resize(size + BIP32_EXTKEY_SIZE); data.resize(size + BIP32_EXTKEY_SIZE);
key.Encode(data.data() + size); key.Encode(data.data() + size);
@ -238,12 +238,12 @@ std::string EncodeExtPubKey(const CExtPubKey& key)
return ret; return ret;
} }
CExtKey DecodeExtKey(const std::string& str) CExtKey KeyIO::DecodeExtKey(const std::string& str)
{ {
CExtKey key; CExtKey key;
std::vector<unsigned char> data; std::vector<unsigned char> data;
if (DecodeBase58Check(str, data)) { if (DecodeBase58Check(str, data)) {
const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY); const std::vector<unsigned char>& prefix = keyConstants.Base58Prefix(KeyConstants::EXT_SECRET_KEY);
if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) { if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) {
key.Decode(data.data() + prefix.size()); key.Decode(data.data() + prefix.size());
} }
@ -251,9 +251,9 @@ CExtKey DecodeExtKey(const std::string& str)
return key; return key;
} }
std::string EncodeExtKey(const CExtKey& key) std::string KeyIO::EncodeExtKey(const CExtKey& key)
{ {
std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY); std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::EXT_SECRET_KEY);
size_t size = data.size(); size_t size = data.size();
data.resize(size + BIP32_EXTKEY_SIZE); data.resize(size + BIP32_EXTKEY_SIZE);
key.Encode(data.data() + size); key.Encode(data.data() + size);
@ -262,40 +262,31 @@ std::string EncodeExtKey(const CExtKey& key)
return ret; return ret;
} }
std::string EncodeDestination(const CTxDestination& dest) std::string KeyIO::EncodeDestination(const CTxDestination& dest)
{ {
return boost::apply_visitor(DestinationEncoder(Params()), dest); return boost::apply_visitor(DestinationEncoder(keyConstants), dest);
} }
CTxDestination DecodeDestination(const std::string& str) bool KeyIO::IsValidDestinationString(const std::string& str)
{ {
return DecodeDestination(str, Params()); return IsValidDestination(DecodeDestination(str));
} }
bool IsValidDestinationString(const std::string& str, const CChainParams& params) std::string KeyIO::EncodePaymentAddress(const libzcash::PaymentAddress& zaddr)
{ {
return IsValidDestination(DecodeDestination(str, params)); return boost::apply_visitor(PaymentAddressEncoder(keyConstants), zaddr);
}
bool IsValidDestinationString(const std::string& str)
{
return IsValidDestinationString(str, Params());
}
std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr)
{
return boost::apply_visitor(PaymentAddressEncoder(Params()), zaddr);
} }
template<typename T1, typename T2, typename T3> template<typename T1, typename T2, typename T3>
T1 DecodeAny( T1 DecodeAny(
const KeyConstants& keyConstants,
const std::string& str, const std::string& str,
std::pair<CChainParams::Base58Type, size_t> sprout, std::pair<KeyConstants::Base58Type, size_t> sprout,
std::pair<CChainParams::Bech32Type, size_t> sapling) std::pair<KeyConstants::Bech32Type, size_t> sapling)
{ {
std::vector<unsigned char> data; std::vector<unsigned char> data;
if (DecodeBase58Check(str, data)) { if (DecodeBase58Check(str, data)) {
const std::vector<unsigned char>& prefix = Params().Base58Prefix(sprout.first); const std::vector<unsigned char>& prefix = keyConstants.Base58Prefix(sprout.first);
if ((data.size() == sprout.second + prefix.size()) && if ((data.size() == sprout.second + prefix.size()) &&
std::equal(prefix.begin(), prefix.end(), data.begin())) { std::equal(prefix.begin(), prefix.end(), data.begin())) {
CSerializeData serialized(data.begin() + prefix.size(), data.end()); CSerializeData serialized(data.begin() + prefix.size(), data.end());
@ -310,7 +301,7 @@ T1 DecodeAny(
data.clear(); data.clear();
auto bech = bech32::Decode(str); auto bech = bech32::Decode(str);
if (bech.first == Params().Bech32HRP(sapling.first) && if (bech.first == keyConstants.Bech32HRP(sapling.first) &&
bech.second.size() == sapling.second) { bech.second.size() == sapling.second) {
// Bech32 decoding // Bech32 decoding
data.reserve((bech.second.size() * 5) / 8); data.reserve((bech.second.size() * 5) / 8);
@ -327,50 +318,53 @@ T1 DecodeAny(
return libzcash::InvalidEncoding(); return libzcash::InvalidEncoding();
} }
libzcash::PaymentAddress DecodePaymentAddress(const std::string& str) libzcash::PaymentAddress KeyIO::DecodePaymentAddress(const std::string& str)
{ {
return DecodeAny<libzcash::PaymentAddress, return DecodeAny<libzcash::PaymentAddress,
libzcash::SproutPaymentAddress, libzcash::SproutPaymentAddress,
libzcash::SaplingPaymentAddress>( libzcash::SaplingPaymentAddress>(
keyConstants,
str, str,
std::make_pair(CChainParams::ZCPAYMENT_ADDRRESS, libzcash::SerializedSproutPaymentAddressSize), std::make_pair(KeyConstants::ZCPAYMENT_ADDRESS, libzcash::SerializedSproutPaymentAddressSize),
std::make_pair(CChainParams::SAPLING_PAYMENT_ADDRESS, ConvertedSaplingPaymentAddressSize) std::make_pair(KeyConstants::SAPLING_PAYMENT_ADDRESS, ConvertedSaplingPaymentAddressSize)
); );
} }
bool IsValidPaymentAddressString(const std::string& str) { bool KeyIO::IsValidPaymentAddressString(const std::string& str) {
return IsValidPaymentAddress(DecodePaymentAddress(str)); return IsValidPaymentAddress(DecodePaymentAddress(str));
} }
std::string EncodeViewingKey(const libzcash::ViewingKey& vk) std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk)
{ {
return boost::apply_visitor(ViewingKeyEncoder(Params()), vk); return boost::apply_visitor(ViewingKeyEncoder(keyConstants), vk);
} }
libzcash::ViewingKey DecodeViewingKey(const std::string& str) libzcash::ViewingKey KeyIO::DecodeViewingKey(const std::string& str)
{ {
return DecodeAny<libzcash::ViewingKey, return DecodeAny<libzcash::ViewingKey,
libzcash::SproutViewingKey, libzcash::SproutViewingKey,
libzcash::SaplingExtendedFullViewingKey>( libzcash::SaplingExtendedFullViewingKey>(
keyConstants,
str, str,
std::make_pair(CChainParams::ZCVIEWING_KEY, libzcash::SerializedSproutViewingKeySize), std::make_pair(KeyConstants::ZCVIEWING_KEY, libzcash::SerializedSproutViewingKeySize),
std::make_pair(CChainParams::SAPLING_EXTENDED_FVK, ConvertedSaplingExtendedFullViewingKeySize) std::make_pair(KeyConstants::SAPLING_EXTENDED_FVK, ConvertedSaplingExtendedFullViewingKeySize)
); );
} }
std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey) std::string KeyIO::EncodeSpendingKey(const libzcash::SpendingKey& zkey)
{ {
return boost::apply_visitor(SpendingKeyEncoder(Params()), zkey); return boost::apply_visitor(SpendingKeyEncoder(keyConstants), zkey);
} }
libzcash::SpendingKey DecodeSpendingKey(const std::string& str) libzcash::SpendingKey KeyIO::DecodeSpendingKey(const std::string& str)
{ {
return DecodeAny<libzcash::SpendingKey, return DecodeAny<libzcash::SpendingKey,
libzcash::SproutSpendingKey, libzcash::SproutSpendingKey,
libzcash::SaplingExtendedSpendingKey>( libzcash::SaplingExtendedSpendingKey>(
keyConstants,
str, str,
std::make_pair(CChainParams::ZCSPENDING_KEY, libzcash::SerializedSproutSpendingKeySize), std::make_pair(KeyConstants::ZCSPENDING_KEY, libzcash::SerializedSproutSpendingKeySize),
std::make_pair(CChainParams::SAPLING_EXTENDED_SPEND_KEY, ConvertedSaplingExtendedSpendingKeySize) std::make_pair(KeyConstants::SAPLING_EXTENDED_SPEND_KEY, ConvertedSaplingExtendedSpendingKeySize)
); );
} }

View File

@ -13,29 +13,38 @@
#include <script/standard.h> #include <script/standard.h>
#include <zcash/Address.hpp> #include <zcash/Address.hpp>
#include <vector>
#include <string> #include <string>
CKey DecodeSecret(const std::string& str); class KeyIO {
std::string EncodeSecret(const CKey& key); private:
const KeyConstants& keyConstants;
CExtKey DecodeExtKey(const std::string& str); public:
std::string EncodeExtKey(const CExtKey& extkey); KeyIO(const KeyConstants& keyConstants): keyConstants(keyConstants) { }
CExtPubKey DecodeExtPubKey(const std::string& str);
std::string EncodeExtPubKey(const CExtPubKey& extpubkey);
std::string EncodeDestination(const CTxDestination& dest); CKey DecodeSecret(const std::string& str);
CTxDestination DecodeDestination(const std::string& str); std::string EncodeSecret(const CKey& key);
bool IsValidDestinationString(const std::string& str);
bool IsValidDestinationString(const std::string& str, const CChainParams& params);
std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr); CExtKey DecodeExtKey(const std::string& str);
libzcash::PaymentAddress DecodePaymentAddress(const std::string& str); std::string EncodeExtKey(const CExtKey& extkey);
bool IsValidPaymentAddressString(const std::string& str); CExtPubKey DecodeExtPubKey(const std::string& str);
std::string EncodeExtPubKey(const CExtPubKey& extpubkey);
std::string EncodeViewingKey(const libzcash::ViewingKey& vk); std::string EncodeDestination(const CTxDestination& dest);
libzcash::ViewingKey DecodeViewingKey(const std::string& str); CTxDestination DecodeDestination(const std::string& str);
std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey); bool IsValidDestinationString(const std::string& str);
libzcash::SpendingKey DecodeSpendingKey(const std::string& str);
std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr);
libzcash::PaymentAddress DecodePaymentAddress(const std::string& str);
bool IsValidPaymentAddressString(const std::string& str);
std::string EncodeViewingKey(const libzcash::ViewingKey& vk);
libzcash::ViewingKey DecodeViewingKey(const std::string& str);
std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey);
libzcash::SpendingKey DecodeSpendingKey(const std::string& str);
};
#endif // BITCOIN_KEYIO_H #endif // BITCOIN_KEYIO_H

View File

@ -15,11 +15,13 @@
#include "checkpoints.h" #include "checkpoints.h"
#include "checkqueue.h" #include "checkqueue.h"
#include "consensus/consensus.h" #include "consensus/consensus.h"
#include "consensus/funding.h"
#include "consensus/upgrades.h" #include "consensus/upgrades.h"
#include "consensus/validation.h" #include "consensus/validation.h"
#include "deprecation.h" #include "deprecation.h"
#include "experimental_features.h" #include "experimental_features.h"
#include "init.h" #include "init.h"
#include "key_io.h"
#include "merkleblock.h" #include "merkleblock.h"
#include "metrics.h" #include "metrics.h"
#include "net.h" #include "net.h"
@ -901,6 +903,18 @@ bool ContextualCheckTransaction(
} }
} }
// From Canopy onward, coinbase transaction must include outputs corresponding to the
// ZIP 207 consensus funding streams active at the current block height. To avoid
// double-decrypting, we detect any shielded funding streams during the Heartwood
// consensus check. If Canopy is not yet active, fundingStreamElements will be empty.
std::set<Consensus::FundingStreamElement> fundingStreamElements;
if (canopyActive) {
fundingStreamElements = Consensus::GetActiveFundingStreamElements(
nHeight,
GetBlockSubsidy(nHeight, chainparams.GetConsensus()),
chainparams.GetConsensus());
}
// Rules that apply to Heartwood or later: // Rules that apply to Heartwood or later:
if (heartwoodActive) { if (heartwoodActive) {
if (tx.IsCoinBase()) { if (tx.IsCoinBase()) {
@ -920,7 +934,6 @@ bool ContextualCheckTransaction(
} }
// SaplingNotePlaintext::decrypt() checks note commitment validity. // SaplingNotePlaintext::decrypt() checks note commitment validity.
auto encPlaintext = SaplingNotePlaintext::decrypt( auto encPlaintext = SaplingNotePlaintext::decrypt(
chainparams.GetConsensus(), chainparams.GetConsensus(),
nHeight, nHeight,
@ -929,6 +942,7 @@ bool ContextualCheckTransaction(
outPlaintext->esk, outPlaintext->esk,
outPlaintext->pk_d, outPlaintext->pk_d,
output.cmu); output.cmu);
if (!encPlaintext) { if (!encPlaintext) {
return state.DoS( return state.DoS(
DOS_LEVEL_BLOCK, DOS_LEVEL_BLOCK,
@ -937,6 +951,18 @@ bool ContextualCheckTransaction(
"bad-cb-output-desc-invalid-encct"); "bad-cb-output-desc-invalid-encct");
} }
// ZIP 207: detect shielded funding stream elements
if (canopyActive) {
libzcash::SaplingPaymentAddress zaddr(encPlaintext->d, outPlaintext->pk_d);
for (auto it = fundingStreamElements.begin(); it != fundingStreamElements.end(); ++it) {
const libzcash::SaplingPaymentAddress* streamAddr = boost::get<libzcash::SaplingPaymentAddress>(&(it->first));
if (streamAddr && zaddr == *streamAddr && encPlaintext->value() == it->second) {
fundingStreamElements.erase(it);
break;
}
}
}
// ZIP 212: Check that the note plaintexts use the v2 note plaintext // ZIP 212: Check that the note plaintexts use the v2 note plaintext
// version. // version.
// This check compels miners to switch to the new plaintext version // This check compels miners to switch to the new plaintext version
@ -959,6 +985,25 @@ bool ContextualCheckTransaction(
return state.DoS(DOS_LEVEL_BLOCK, error("ContextualCheckTransaction(): joinsplit.vpub_old nonzero"), REJECT_INVALID, "bad-txns-vpub_old-nonzero"); return state.DoS(DOS_LEVEL_BLOCK, error("ContextualCheckTransaction(): joinsplit.vpub_old nonzero"), REJECT_INVALID, "bad-txns-vpub_old-nonzero");
} }
} }
if (tx.IsCoinBase()) {
// Detect transparent funding streams.
for (const CTxOut& output : tx.vout) {
for (auto it = fundingStreamElements.begin(); it != fundingStreamElements.end(); ++it) {
const CScript* taddr = boost::get<CScript>(&(it->first));
if (taddr && output.scriptPubKey == *taddr && output.nValue == it->second) {
fundingStreamElements.erase(it);
break;
}
}
}
if (!fundingStreamElements.empty()) {
std::cout << "\nFunding stream missing at height " << nHeight;
return state.DoS(100, error("%s: funding stream missing", __func__),
REJECT_INVALID, "cb-funding-stream-missing");
}
}
} }
auto consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus()); auto consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus());
@ -1821,7 +1866,7 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
// Mining slow start // Mining slow start
// The subsidy is ramped up linearly, skipping the middle payout of // The subsidy is ramped up linearly, skipping the middle payout of
// MAX_SUBSIDY/2 to keep the monetary curve consistent with no slow start. // MAX_SUBSIDY/2 to keep the monetary curve consistent with no slow start.
if (nHeight < consensusParams.nSubsidySlowStartInterval / 2) { if (nHeight < consensusParams.SubsidySlowStartShift()) {
nSubsidy /= consensusParams.nSubsidySlowStartInterval; nSubsidy /= consensusParams.nSubsidySlowStartInterval;
nSubsidy *= nHeight; nSubsidy *= nHeight;
return nSubsidy; return nSubsidy;
@ -1831,7 +1876,7 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
return nSubsidy; return nSubsidy;
} }
assert(nHeight > consensusParams.SubsidySlowStartShift()); assert(nHeight >= consensusParams.SubsidySlowStartShift());
int halvings = consensusParams.Halving(nHeight); int halvings = consensusParams.Halving(nHeight);
@ -4132,12 +4177,14 @@ bool ContextualCheckBlock(
} }
} }
// Coinbase transaction must include an output sending 20% of if (consensusParams.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_CANOPY)) {
// the block subsidy to a Founders' Reward script, until the last Founders' // Funding streams are checked inside ContextualCheckTransaction.
// Reward block is reached, with exception of the genesis block. } else if ((nHeight > 0) && (nHeight <= consensusParams.GetLastFoundersRewardBlockHeight(nHeight))) {
// The last Founders' Reward block is defined as the block just before the // Coinbase transaction must include an output sending 20% of
// first subsidy halving block, which occurs at halving_interval + slow_start_shift. // the block subsidy to a Founders' Reward script, until the last Founders'
if ((nHeight > 0) && (nHeight <= consensusParams.GetLastFoundersRewardBlockHeight(nHeight))) { // Reward block is reached, with exception of the genesis block.
// The last Founders' Reward block is defined as the block just before the
// first subsidy halving block, which occurs at halving_interval + slow_start_shift.
bool found = false; bool found = false;
BOOST_FOREACH(const CTxOut& output, block.vtx[0].vout) { BOOST_FOREACH(const CTxOut& output, block.vtx[0].vout) {

View File

@ -465,7 +465,10 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state,
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins,
const CChainParams& chainparams, bool fJustCheck = false); const CChainParams& chainparams, bool fJustCheck = false);
/** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */ /**
* Check a block is completely valid from start to finish (only works on top
* of our current best block, with cs_main held)
*/
bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true); bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true);

View File

@ -11,6 +11,7 @@
#include "amount.h" #include "amount.h"
#include "chainparams.h" #include "chainparams.h"
#include "consensus/consensus.h" #include "consensus/consensus.h"
#include "consensus/funding.h"
#include "consensus/upgrades.h" #include "consensus/upgrades.h"
#include "consensus/validation.h" #include "consensus/validation.h"
#ifdef ENABLE_MINING #ifdef ENABLE_MINING
@ -21,6 +22,7 @@
#include "main.h" #include "main.h"
#include "metrics.h" #include "metrics.h"
#include "net.h" #include "net.h"
#include "zcash/Note.hpp"
#include "policy/policy.h" #include "policy/policy.h"
#include "pow.h" #include "pow.h"
#include "primitives/transaction.h" #include "primitives/transaction.h"
@ -119,6 +121,42 @@ bool IsValidMinerAddress(const MinerAddress& minerAddr) {
return minerAddr.which() != 0; return minerAddr.which() != 0;
} }
class AddFundingStreamValueToTx : public boost::static_visitor<bool>
{
private:
CMutableTransaction &mtx;
void* ctx;
const CAmount fundingStreamValue;
const libzcash::Zip212Enabled zip212Enabled;
public:
AddFundingStreamValueToTx(
CMutableTransaction &mtx,
void* ctx,
const CAmount fundingStreamValue,
const libzcash::Zip212Enabled zip212Enabled): mtx(mtx), ctx(ctx), fundingStreamValue(fundingStreamValue), zip212Enabled(zip212Enabled) {}
bool operator()(const libzcash::SaplingPaymentAddress& pa) const {
uint256 ovk;
auto note = libzcash::SaplingNote(pa, fundingStreamValue, zip212Enabled);
auto output = OutputDescriptionInfo(ovk, note, NO_MEMO);
auto odesc = output.Build(ctx);
if (odesc) {
mtx.vShieldedOutput.push_back(odesc.get());
mtx.valueBalance -= fundingStreamValue;
return true;
} else {
return false;
}
}
bool operator()(const CScript& scriptPubKey) const {
mtx.vout.push_back(CTxOut(fundingStreamValue, scriptPubKey));
return true;
}
};
class AddOutputsToCoinbaseTxAndSign : public boost::static_visitor<> class AddOutputsToCoinbaseTxAndSign : public boost::static_visitor<>
{ {
private: private:
@ -134,45 +172,50 @@ public:
const int nHeight, const int nHeight,
const CAmount nFees) : mtx(mtx), chainparams(chainparams), nHeight(nHeight), nFees(nFees) {} const CAmount nFees) : mtx(mtx), chainparams(chainparams), nHeight(nHeight), nFees(nFees) {}
CAmount SetFoundersRewardAndGetMinerValue() const { const libzcash::Zip212Enabled GetZip212Flag() const {
auto value = GetBlockSubsidy(nHeight, chainparams.GetConsensus()); if (chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_CANOPY)) {
return libzcash::Zip212Enabled::AfterZip212;
if ((nHeight > 0) && (nHeight <= chainparams.GetConsensus().GetLastFoundersRewardBlockHeight(nHeight))) { } else {
// Founders reward is 20% of the block subsidy return libzcash::Zip212Enabled::BeforeZip212;
auto vFoundersReward = value / 5;
// Take some reward away from us
value -= vFoundersReward;
// And give it to the founders
mtx.vout.push_back(CTxOut(vFoundersReward, chainparams.GetFoundersRewardScriptAtHeight(nHeight)));
} }
return value + nFees;
} }
void operator()(const InvalidMinerAddress &invalid) const {} CAmount SetFoundersRewardAndGetMinerValue(void* ctx) const {
auto block_subsidy = GetBlockSubsidy(nHeight, chainparams.GetConsensus());
auto miner_reward = block_subsidy; // founders' reward or funding stream amounts will be subtracted below
// Create shielded output if (nHeight > 0) {
void operator()(const libzcash::SaplingPaymentAddress &pa) const { if (chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_CANOPY)) {
auto value = SetFoundersRewardAndGetMinerValue(); auto fundingStreamElements = Consensus::GetActiveFundingStreamElements(
mtx.valueBalance = -value; nHeight,
block_subsidy,
chainparams.GetConsensus());
uint256 ovk; for (Consensus::FundingStreamElement fselem : fundingStreamElements) {
libzcash::Zip212Enabled zip_212_enabled = libzcash::Zip212Enabled::BeforeZip212; miner_reward -= fselem.second;
if (Params().GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_CANOPY)) { bool added = boost::apply_visitor(AddFundingStreamValueToTx(mtx, ctx, fselem.second, GetZip212Flag()), fselem.first);
zip_212_enabled = libzcash::Zip212Enabled::AfterZip212; if (!added) {
librustzcash_sapling_proving_ctx_free(ctx);
throw new std::runtime_error("Failed to add funding stream output.");
}
}
} else if (nHeight <= chainparams.GetConsensus().GetLastFoundersRewardBlockHeight(nHeight)) {
// Founders reward is 20% of the block subsidy
auto vFoundersReward = miner_reward / 5;
// Take some reward away from us
miner_reward -= vFoundersReward;
// And give it to the founders
mtx.vout.push_back(CTxOut(vFoundersReward, chainparams.GetFoundersRewardScriptAtHeight(nHeight)));
} else {
// Founders reward ends without replacement if Canopy is not activated by the
// last Founders' Reward block height + 1.
}
} }
auto note = libzcash::SaplingNote(pa, value, zip_212_enabled);
auto output = OutputDescriptionInfo(ovk, note, {{0xF6}});
auto ctx = librustzcash_sapling_proving_ctx_init(); return miner_reward + nFees;
}
auto odesc = output.Build(ctx);
if (!odesc) {
librustzcash_sapling_proving_ctx_free(ctx);
throw new std::runtime_error("Failed to create shielded output for miner");
}
mtx.vShieldedOutput.push_back(odesc.get());
void ComputeBindingSig(void* ctx) const {
// Empty output script. // Empty output script.
uint256 dataToBeSigned; uint256 dataToBeSigned;
CScript scriptCode; CScript scriptCode;
@ -185,24 +228,62 @@ public:
throw ex; throw ex;
} }
librustzcash_sapling_binding_sig( bool success = librustzcash_sapling_binding_sig(
ctx, ctx,
mtx.valueBalance, mtx.valueBalance,
dataToBeSigned.begin(), dataToBeSigned.begin(),
mtx.bindingSig.data()); mtx.bindingSig.data());
if (!success) {
librustzcash_sapling_proving_ctx_free(ctx);
throw new std::runtime_error("An error occurred computing the binding signature.");
}
}
void operator()(const InvalidMinerAddress &invalid) const {}
// Create shielded output
void operator()(const libzcash::SaplingPaymentAddress &pa) const {
auto ctx = librustzcash_sapling_proving_ctx_init();
auto miner_reward = SetFoundersRewardAndGetMinerValue(ctx);
mtx.valueBalance -= miner_reward;
uint256 ovk;
auto note = libzcash::SaplingNote(pa, miner_reward, GetZip212Flag());
auto output = OutputDescriptionInfo(ovk, note, NO_MEMO);
auto odesc = output.Build(ctx);
if (!odesc) {
librustzcash_sapling_proving_ctx_free(ctx);
throw new std::runtime_error("Failed to create shielded output for miner");
}
mtx.vShieldedOutput.push_back(odesc.get());
ComputeBindingSig(ctx);
librustzcash_sapling_proving_ctx_free(ctx); librustzcash_sapling_proving_ctx_free(ctx);
} }
// Create transparent output // Create transparent output
void operator()(const boost::shared_ptr<CReserveScript> &coinbaseScript) const { void operator()(const boost::shared_ptr<CReserveScript> &coinbaseScript) const {
// Create the miner's output.
mtx.vout.resize(1);
// Add the FR output and fetch the miner's output value. // Add the FR output and fetch the miner's output value.
auto value = SetFoundersRewardAndGetMinerValue(); auto ctx = librustzcash_sapling_proving_ctx_init();
// Miner output will be vout[0]; Founders' Reward & funding stream outputs
// will follow.
mtx.vout.resize(1);
auto value = SetFoundersRewardAndGetMinerValue(ctx);
// Now fill in the miner's output. // Now fill in the miner's output.
mtx.vout[0].nValue = value; mtx.vout[0] = CTxOut(value, coinbaseScript->reserveScript);
mtx.vout[0].scriptPubKey = coinbaseScript->reserveScript;
if (mtx.vShieldedOutput.size() > 0) {
ComputeBindingSig(ctx);
}
librustzcash_sapling_proving_ctx_free(ctx);
} }
}; };
@ -540,7 +621,7 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddre
CValidationState state; CValidationState state;
if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false))
throw std::runtime_error("CreateNewBlock(): TestBlockValidity failed"); throw std::runtime_error(std::string("CreateNewBlock(): TestBlockValidity failed: ") + state.GetRejectReason());
} }
return pblocktemplate.release(); return pblocktemplate.release();
@ -565,9 +646,11 @@ class MinerAddressScript : public CReserveScript
void GetMinerAddress(MinerAddress &minerAddress) void GetMinerAddress(MinerAddress &minerAddress)
{ {
KeyIO keyIO(Params());
// Try a transparent address first // Try a transparent address first
auto mAddrArg = GetArg("-mineraddress", ""); auto mAddrArg = GetArg("-mineraddress", "");
CTxDestination addr = DecodeDestination(mAddrArg); CTxDestination addr = keyIO.DecodeDestination(mAddrArg);
if (IsValidDestination(addr)) { if (IsValidDestination(addr)) {
boost::shared_ptr<MinerAddressScript> mAddr(new MinerAddressScript()); boost::shared_ptr<MinerAddressScript> mAddr(new MinerAddressScript());
CKeyID keyID = boost::get<CKeyID>(addr); CKeyID keyID = boost::get<CKeyID>(addr);
@ -576,7 +659,7 @@ void GetMinerAddress(MinerAddress &minerAddress)
minerAddress = mAddr; minerAddress = mAddr;
} else { } else {
// Try a Sapling address // Try a Sapling address
auto zaddr = DecodePaymentAddress(mAddrArg); auto zaddr = keyIO.DecodePaymentAddress(mAddrArg);
if (IsValidPaymentAddress(zaddr)) { if (IsValidPaymentAddress(zaddr)) {
if (boost::get<libzcash::SaplingPaymentAddress>(&zaddr) != nullptr) { if (boost::get<libzcash::SaplingPaymentAddress>(&zaddr) != nullptr) {
minerAddress = boost::get<libzcash::SaplingPaymentAddress>(zaddr); minerAddress = boost::get<libzcash::SaplingPaymentAddress>(zaddr);

View File

@ -143,6 +143,7 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex)
result.pushKV("version", block.nVersion); result.pushKV("version", block.nVersion);
result.pushKV("merkleroot", block.hashMerkleRoot.GetHex()); result.pushKV("merkleroot", block.hashMerkleRoot.GetHex());
KeyIO keyIO(Params());
UniValue deltas(UniValue::VARR); UniValue deltas(UniValue::VARR);
for (unsigned int i = 0; i < block.vtx.size(); i++) { for (unsigned int i = 0; i < block.vtx.size(); i++) {
const CTransaction &tx = block.vtx[i]; const CTransaction &tx = block.vtx[i];
@ -165,7 +166,7 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex)
} }
CTxDestination dest = DestFromAddressHash(spentInfo.addressType, spentInfo.addressHash); CTxDestination dest = DestFromAddressHash(spentInfo.addressType, spentInfo.addressHash);
if (IsValidDestination(dest)) { if (IsValidDestination(dest)) {
delta.pushKV("address", EncodeDestination(dest)); delta.pushKV("address", keyIO.EncodeDestination(dest));
} }
delta.pushKV("satoshis", -1 * spentInfo.satoshis); delta.pushKV("satoshis", -1 * spentInfo.satoshis);
delta.pushKV("index", (int)j); delta.pushKV("index", (int)j);
@ -190,9 +191,9 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex)
dest = CKeyID(addrhash); dest = CKeyID(addrhash);
} }
if (IsValidDestination(dest)) { if (IsValidDestination(dest)) {
delta.pushKV("address", EncodeDestination(dest)); delta.pushKV("address", keyIO.EncodeDestination(dest));
} }
delta.pushKV("address", EncodeDestination(dest)); delta.pushKV("address", keyIO.EncodeDestination(dest));
delta.pushKV("satoshis", out.nValue); delta.pushKV("satoshis", out.nValue);
delta.pushKV("index", (int)k); delta.pushKV("index", (int)k);

View File

@ -6,6 +6,7 @@
#include "amount.h" #include "amount.h"
#include "chainparams.h" #include "chainparams.h"
#include "consensus/consensus.h" #include "consensus/consensus.h"
#include "consensus/funding.h"
#include "consensus/validation.h" #include "consensus/validation.h"
#include "core_io.h" #include "core_io.h"
#ifdef ENABLE_MINING #ifdef ENABLE_MINING
@ -527,6 +528,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
// TestBlockValidity only supports blocks built on the current Tip // TestBlockValidity only supports blocks built on the current Tip
if (block.hashPrevBlock != pindexPrev->GetBlockHash()) if (block.hashPrevBlock != pindexPrev->GetBlockHash())
return "inconclusive-not-best-prevblk"; return "inconclusive-not-best-prevblk";
CValidationState state; CValidationState state;
TestBlockValidity(state, Params(), block, pindexPrev, false, true); TestBlockValidity(state, Params(), block, pindexPrev, false, true);
return BIP22ValidationResult(state); return BIP22ValidationResult(state);
@ -877,8 +879,15 @@ UniValue getblocksubsidy(const UniValue& params, bool fHelp)
"1. height (numeric, optional) The block height. If not provided, defaults to the current height of the chain.\n" "1. height (numeric, optional) The block height. If not provided, defaults to the current height of the chain.\n"
"\nResult:\n" "\nResult:\n"
"{\n" "{\n"
" \"miner\" : x.xxx (numeric) The mining reward amount in " + CURRENCY_UNIT + ".\n" " \"miner\" : x.xxx, (numeric) The mining reward amount in " + CURRENCY_UNIT + ".\n"
" \"founders\" : x.xxx (numeric) The founders reward amount in " + CURRENCY_UNIT + ".\n" " \"founders\" : x.xxx, (numeric) The founders' reward amount in " + CURRENCY_UNIT + ".\n"
" \"fundingstreams\" : [ (array) An array of funding stream descriptions (present only when Canopy has activated).\n"
" {\n"
" \"recipient\" : \"...\", (string) A description of the funding stream recipient.\n"
" \"specification\" : \"url\", (string) A URL for the specification of this funding stream.\n"
" \"value\" : x.xxx (numeric) The funding stream amount in " + CURRENCY_UNIT + ".\n"
" }, ...\n"
" ]\n"
"}\n" "}\n"
"\nExamples:\n" "\nExamples:\n"
+ HelpExampleCli("getblocksubsidy", "1000") + HelpExampleCli("getblocksubsidy", "1000")
@ -890,14 +899,32 @@ UniValue getblocksubsidy(const UniValue& params, bool fHelp)
if (nHeight < 0) if (nHeight < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
CAmount nReward = GetBlockSubsidy(nHeight, Params().GetConsensus()); auto consensus = Params().GetConsensus();
CAmount nBlockSubsidy = GetBlockSubsidy(nHeight, consensus);
CAmount nMinerReward = nBlockSubsidy;
CAmount nFoundersReward = 0; CAmount nFoundersReward = 0;
if ((nHeight > 0) && (nHeight <= Params().GetConsensus().GetLastFoundersRewardBlockHeight(nHeight))) { bool canopyActive = consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_CANOPY);
nFoundersReward = nReward/5;
nReward -= nFoundersReward;
}
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
result.pushKV("miner", ValueFromAmount(nReward)); if (canopyActive) {
UniValue fundingstreams(UniValue::VARR);
auto fsinfos = Consensus::GetActiveFundingStreams(nHeight, consensus);
for (auto fsinfo : fsinfos) {
CAmount nStreamAmount = fsinfo.Value(nBlockSubsidy);
nMinerReward -= nStreamAmount;
UniValue fsobj(UniValue::VOBJ);
fsobj.pushKV("recipient", fsinfo.recipient);
fsobj.pushKV("specification", fsinfo.specification);
fsobj.pushKV("value", ValueFromAmount(nStreamAmount));
fundingstreams.push_back(fsobj);
}
result.pushKV("fundingstreams", fundingstreams);
} else if (nHeight > 0 && nHeight <= consensus.GetLastFoundersRewardBlockHeight(nHeight)) {
nFoundersReward = nBlockSubsidy/5;
nMinerReward -= nFoundersReward;
}
result.pushKV("miner", ValueFromAmount(nMinerReward));
result.pushKV("founders", ValueFromAmount(nFoundersReward)); result.pushKV("founders", ValueFromAmount(nFoundersReward));
return result; return result;
} }

View File

@ -127,6 +127,7 @@ public:
} }
UniValue operator()(const CScriptID &scriptID) const { UniValue operator()(const CScriptID &scriptID) const {
KeyIO keyIO(Params());
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
CScript subscript; CScript subscript;
obj.pushKV("isscript", true); obj.pushKV("isscript", true);
@ -139,7 +140,7 @@ public:
obj.pushKV("hex", HexStr(subscript.begin(), subscript.end())); obj.pushKV("hex", HexStr(subscript.begin(), subscript.end()));
UniValue a(UniValue::VARR); UniValue a(UniValue::VARR);
for (const CTxDestination& addr : addresses) { for (const CTxDestination& addr : addresses) {
a.push_back(EncodeDestination(addr)); a.push_back(keyIO.EncodeDestination(addr));
} }
obj.pushKV("addresses", a); obj.pushKV("addresses", a);
if (whichType == TX_MULTISIG) if (whichType == TX_MULTISIG)
@ -180,14 +181,15 @@ UniValue validateaddress(const UniValue& params, bool fHelp)
LOCK(cs_main); LOCK(cs_main);
#endif #endif
CTxDestination dest = DecodeDestination(params[0].get_str()); KeyIO keyIO(Params());
CTxDestination dest = keyIO.DecodeDestination(params[0].get_str());
bool isValid = IsValidDestination(dest); bool isValid = IsValidDestination(dest);
UniValue ret(UniValue::VOBJ); UniValue ret(UniValue::VOBJ);
ret.pushKV("isvalid", isValid); ret.pushKV("isvalid", isValid);
if (isValid) if (isValid)
{ {
std::string currentAddress = EncodeDestination(dest); std::string currentAddress = keyIO.EncodeDestination(dest);
ret.pushKV("address", currentAddress); ret.pushKV("address", currentAddress);
CScript scriptPubKey = GetScriptForDestination(dest); CScript scriptPubKey = GetScriptForDestination(dest);
@ -271,8 +273,9 @@ UniValue z_validateaddress(const UniValue& params, bool fHelp)
LOCK(cs_main); LOCK(cs_main);
#endif #endif
KeyIO keyIO(Params());
string strAddress = params[0].get_str(); string strAddress = params[0].get_str();
auto address = DecodePaymentAddress(strAddress); auto address = keyIO.DecodePaymentAddress(strAddress);
bool isValid = IsValidPaymentAddress(address); bool isValid = IsValidPaymentAddress(address);
UniValue ret(UniValue::VOBJ); UniValue ret(UniValue::VOBJ);
@ -304,6 +307,9 @@ CScript _createmultisig_redeemScript(const UniValue& params)
"(got %u keys, but need at least %d to redeem)", keys.size(), nRequired)); "(got %u keys, but need at least %d to redeem)", keys.size(), nRequired));
if (keys.size() > 16) if (keys.size() > 16)
throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number"); throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number");
KeyIO keyIO(Params());
std::vector<CPubKey> pubkeys; std::vector<CPubKey> pubkeys;
pubkeys.resize(keys.size()); pubkeys.resize(keys.size());
for (unsigned int i = 0; i < keys.size(); i++) for (unsigned int i = 0; i < keys.size(); i++)
@ -311,7 +317,7 @@ CScript _createmultisig_redeemScript(const UniValue& params)
const std::string& ks = keys[i].get_str(); const std::string& ks = keys[i].get_str();
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
// Case 1: Bitcoin address and we have full public key: // Case 1: Bitcoin address and we have full public key:
CTxDestination dest = DecodeDestination(ks); CTxDestination dest = keyIO.DecodeDestination(ks);
if (pwalletMain && IsValidDestination(dest)) { if (pwalletMain && IsValidDestination(dest)) {
const CKeyID *keyID = boost::get<CKeyID>(&dest); const CKeyID *keyID = boost::get<CKeyID>(&dest);
if (!keyID) { if (!keyID) {
@ -385,8 +391,9 @@ UniValue createmultisig(const UniValue& params, bool fHelp)
CScript inner = _createmultisig_redeemScript(params); CScript inner = _createmultisig_redeemScript(params);
CScriptID innerID(inner); CScriptID innerID(inner);
KeyIO keyIO(Params());
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
result.pushKV("address", EncodeDestination(innerID)); result.pushKV("address", keyIO.EncodeDestination(innerID));
result.pushKV("redeemScript", HexStr(inner.begin(), inner.end())); result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
return result; return result;
@ -421,7 +428,8 @@ UniValue verifymessage(const UniValue& params, bool fHelp)
string strSign = params[1].get_str(); string strSign = params[1].get_str();
string strMessage = params[2].get_str(); string strMessage = params[2].get_str();
CTxDestination destination = DecodeDestination(strAddress); KeyIO keyIO(Params());
CTxDestination destination = keyIO.DecodeDestination(strAddress);
if (!IsValidDestination(destination)) { if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
} }
@ -506,10 +514,11 @@ UniValue getexperimentalfeatures(const UniValue& params, bool fHelp)
static bool getAddressFromIndex( static bool getAddressFromIndex(
int type, const uint160 &hash, std::string &address) int type, const uint160 &hash, std::string &address)
{ {
KeyIO keyIO(Params());
if (type == CScript::P2SH) { if (type == CScript::P2SH) {
address = EncodeDestination(CScriptID(hash)); address = keyIO.EncodeDestination(CScriptID(hash));
} else if (type == CScript::P2PKH) { } else if (type == CScript::P2PKH) {
address = EncodeDestination(CKeyID(hash)); address = keyIO.EncodeDestination(CKeyID(hash));
} else { } else {
return false; return false;
} }
@ -560,8 +569,10 @@ static bool getAddressesFromParams(
} else { } else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
} }
KeyIO keyIO(Params());
for (const auto& it : param_addresses) { for (const auto& it : param_addresses) {
CTxDestination address = DecodeDestination(it); CTxDestination address = keyIO.DecodeDestination(it);
uint160 hashBytes; uint160 hashBytes;
int type = 0; int type = 0;
if (!getIndexKey(address, hashBytes, type)) { if (!getIndexKey(address, hashBytes, type)) {

View File

@ -51,9 +51,10 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fInclud
out.pushKV("reqSigs", nRequired); out.pushKV("reqSigs", nRequired);
out.pushKV("type", GetTxnOutputType(type)); out.pushKV("type", GetTxnOutputType(type));
KeyIO keyIO(Params());
UniValue a(UniValue::VARR); UniValue a(UniValue::VARR);
for (const CTxDestination& addr : addresses) { for (const CTxDestination& addr : addresses) {
a.push_back(EncodeDestination(addr)); a.push_back(keyIO.EncodeDestination(addr));
} }
out.pushKV("addresses", a); out.pushKV("addresses", a);
} }
@ -161,6 +162,8 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
if (tx.fOverwintered) { if (tx.fOverwintered) {
entry.pushKV("expiryheight", (int64_t)tx.nExpiryHeight); entry.pushKV("expiryheight", (int64_t)tx.nExpiryHeight);
} }
KeyIO keyIO(Params());
UniValue vin(UniValue::VARR); UniValue vin(UniValue::VARR);
BOOST_FOREACH(const CTxIn& txin, tx.vin) { BOOST_FOREACH(const CTxIn& txin, tx.vin) {
UniValue in(UniValue::VOBJ); UniValue in(UniValue::VOBJ);
@ -184,7 +187,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
CTxDestination dest = CTxDestination dest =
DestFromAddressHash(spentInfo.addressType, spentInfo.addressHash); DestFromAddressHash(spentInfo.addressType, spentInfo.addressHash);
if (IsValidDestination(dest)) { if (IsValidDestination(dest)) {
in.pushKV("address", EncodeDestination(dest)); in.pushKV("address", keyIO.EncodeDestination(dest));
} }
} }
} }
@ -585,10 +588,11 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
rawTx.vin.push_back(in); rawTx.vin.push_back(in);
} }
KeyIO keyIO(Params());
std::set<CTxDestination> destinations; std::set<CTxDestination> destinations;
vector<string> addrList = sendTo.getKeys(); vector<string> addrList = sendTo.getKeys();
for (const std::string& name_ : addrList) { for (const std::string& name_ : addrList) {
CTxDestination destination = DecodeDestination(name_); CTxDestination destination = keyIO.DecodeDestination(name_);
if (!IsValidDestination(destination)) { if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Zcash address: ") + name_); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Zcash address: ") + name_);
} }
@ -740,7 +744,8 @@ UniValue decodescript(const UniValue& params, bool fHelp)
} }
ScriptPubKeyToJSON(script, r, false); ScriptPubKeyToJSON(script, r, false);
r.pushKV("p2sh", EncodeDestination(CScriptID(script))); KeyIO keyIO(Params());
r.pushKV("p2sh", keyIO.EncodeDestination(CScriptID(script)));
return r; return r;
} }
@ -865,6 +870,8 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
} }
KeyIO keyIO(Params());
bool fGivenKeys = false; bool fGivenKeys = false;
CBasicKeyStore tempKeystore; CBasicKeyStore tempKeystore;
if (params.size() > 2 && !params[2].isNull()) { if (params.size() > 2 && !params[2].isNull()) {
@ -872,7 +879,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
UniValue keys = params[2].get_array(); UniValue keys = params[2].get_array();
for (size_t idx = 0; idx < keys.size(); idx++) { for (size_t idx = 0; idx < keys.size(); idx++) {
UniValue k = keys[idx]; UniValue k = keys[idx];
CKey key = DecodeSecret(k.get_str()); CKey key = keyIO.DecodeSecret(k.get_str());
if (!key.IsValid()) if (!key.IsValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
tempKeystore.AddKey(key); tempKeystore.AddKey(key);

View File

@ -100,26 +100,28 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
} else { } else {
SelectParams(CBaseChainParams::MAIN); SelectParams(CBaseChainParams::MAIN);
} }
KeyIO keyIO(Params());
if (isPrivkey) { if (isPrivkey) {
bool isCompressed = find_value(metadata, "isCompressed").get_bool(); bool isCompressed = find_value(metadata, "isCompressed").get_bool();
// Must be valid private key // Must be valid private key
privkey = DecodeSecret(exp_base58string); privkey = keyIO.DecodeSecret(exp_base58string);
BOOST_CHECK_MESSAGE(privkey.IsValid(), "!IsValid:" + strTest); BOOST_CHECK_MESSAGE(privkey.IsValid(), "!IsValid:" + strTest);
BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed, "compressed mismatch:" + strTest); BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed, "compressed mismatch:" + strTest);
BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest); BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest);
// Private key must be invalid public key // Private key must be invalid public key
destination = DecodeDestination(exp_base58string); destination = keyIO.DecodeDestination(exp_base58string);
BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest); BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest);
} else { } else {
// Must be valid public key // Must be valid public key
destination = DecodeDestination(exp_base58string); destination = keyIO.DecodeDestination(exp_base58string);
CScript script = GetScriptForDestination(destination); CScript script = GetScriptForDestination(destination);
BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest); BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest);
BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
// Public key must be invalid private key // Public key must be invalid private key
privkey = DecodeSecret(exp_base58string); privkey = keyIO.DecodeSecret(exp_base58string);
BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid pubkey as privkey:" + strTest); BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid pubkey as privkey:" + strTest);
} }
} }
@ -148,17 +150,19 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
} else { } else {
SelectParams(CBaseChainParams::MAIN); SelectParams(CBaseChainParams::MAIN);
} }
KeyIO keyIO(Params());
if (isPrivkey) { if (isPrivkey) {
bool isCompressed = find_value(metadata, "isCompressed").get_bool(); bool isCompressed = find_value(metadata, "isCompressed").get_bool();
CKey key; CKey key;
key.Set(exp_payload.begin(), exp_payload.end(), isCompressed); key.Set(exp_payload.begin(), exp_payload.end(), isCompressed);
assert(key.IsValid()); assert(key.IsValid());
BOOST_CHECK_MESSAGE(EncodeSecret(key) == exp_base58string, "result mismatch: " + strTest); BOOST_CHECK_MESSAGE(keyIO.EncodeSecret(key) == exp_base58string, "result mismatch: " + strTest);
} else { } else {
CTxDestination dest; CTxDestination dest;
CScript exp_script(exp_payload.begin(), exp_payload.end()); CScript exp_script(exp_payload.begin(), exp_payload.end());
ExtractDestination(exp_script, dest); ExtractDestination(exp_script, dest);
std::string address = EncodeDestination(dest); std::string address = keyIO.EncodeDestination(dest);
BOOST_CHECK_EQUAL(address, exp_base58string); BOOST_CHECK_EQUAL(address, exp_base58string);
} }
} }
@ -173,6 +177,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid)
CKey privkey; CKey privkey;
CTxDestination destination; CTxDestination destination;
KeyIO keyIO(Params());
for (size_t idx = 0; idx < tests.size(); idx++) { for (size_t idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx]; UniValue test = tests[idx];
std::string strTest = test.write(); std::string strTest = test.write();
@ -184,9 +189,9 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid)
std::string exp_base58string = test[0].get_str(); std::string exp_base58string = test[0].get_str();
// must be invalid as public and as private key // must be invalid as public and as private key
destination = DecodeDestination(exp_base58string); destination = keyIO.DecodeDestination(exp_base58string);
BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey:" + strTest); BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey:" + strTest);
privkey = DecodeSecret(exp_base58string); privkey = keyIO.DecodeSecret(exp_base58string);
BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid privkey:" + strTest); BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid privkey:" + strTest);
} }
} }

View File

@ -84,18 +84,19 @@ void RunTest(const TestVector &test) {
CExtPubKey pubkey; CExtPubKey pubkey;
key.SetMaster(&seed[0], seed.size()); key.SetMaster(&seed[0], seed.size());
pubkey = key.Neuter(); pubkey = key.Neuter();
KeyIO keyIO(Params());
BOOST_FOREACH(const TestDerivation &derive, test.vDerive) { BOOST_FOREACH(const TestDerivation &derive, test.vDerive) {
unsigned char data[74]; unsigned char data[74];
key.Encode(data); key.Encode(data);
pubkey.Encode(data); pubkey.Encode(data);
// Test private key // Test private key
BOOST_CHECK(EncodeExtKey(key) == derive.prv); BOOST_CHECK(keyIO.EncodeExtKey(key) == derive.prv);
BOOST_CHECK(DecodeExtKey(derive.prv) == key); //ensure a base58 decoded key also matches BOOST_CHECK(keyIO.DecodeExtKey(derive.prv) == key); //ensure a base58 decoded key also matches
// Test public key // Test public key
BOOST_CHECK(EncodeExtPubKey(pubkey) == derive.pub); BOOST_CHECK(keyIO.EncodeExtPubKey(pubkey) == derive.pub);
BOOST_CHECK(DecodeExtPubKey(derive.pub) == pubkey); //ensure a base58 decoded pubkey also matches BOOST_CHECK(keyIO.DecodeExtPubKey(derive.pub) == pubkey); //ensure a base58 decoded pubkey also matches
// Derive new keys // Derive new keys
CExtKey keyNew; CExtKey keyNew;

View File

@ -86,8 +86,9 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak)
BOOST_AUTO_TEST_CASE(bloom_create_insert_key) BOOST_AUTO_TEST_CASE(bloom_create_insert_key)
{ {
KeyIO keyIO(Params());
std::string strSecret = std::string("5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"); std::string strSecret = std::string("5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C");
CKey key = DecodeSecret(strSecret); CKey key = keyIO.DecodeSecret(strSecret);
CPubKey pubkey = key.GetPubKey(); CPubKey pubkey = key.GetPubKey();
vector<unsigned char> vchPubKey(pubkey.begin(), pubkey.end()); vector<unsigned char> vchPubKey(pubkey.begin(), pubkey.end());

View File

@ -46,6 +46,7 @@ void dumpKeyInfo(uint256 privkey)
memcpy(&sec[0], &secret[0], 32); memcpy(&sec[0], &secret[0], 32);
printf(" * secret (hex): %s\n", HexStr(sec).c_str()); printf(" * secret (hex): %s\n", HexStr(sec).c_str());
KeyIO keyIO(Params());
for (int nCompressed=0; nCompressed<2; nCompressed++) for (int nCompressed=0; nCompressed<2; nCompressed++)
{ {
bool fCompressed = nCompressed == 1; bool fCompressed = nCompressed == 1;
@ -57,7 +58,7 @@ void dumpKeyInfo(uint256 privkey)
key.SetSecret(secret, fCompressed); key.SetSecret(secret, fCompressed);
vector<unsigned char> vchPubKey = key.GetPubKey(); vector<unsigned char> vchPubKey = key.GetPubKey();
printf(" * pubkey (hex): %s\n", HexStr(vchPubKey).c_str()); printf(" * pubkey (hex): %s\n", HexStr(vchPubKey).c_str());
printf(" * address (base58): %s\n", EncodeDestination(vchPubKey).c_str()); printf(" * address (base58): %s\n", keyIO.EncodeDestination(vchPubKey).c_str());
} }
} }
#endif #endif
@ -67,15 +68,16 @@ BOOST_FIXTURE_TEST_SUITE(key_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(key_test1) BOOST_AUTO_TEST_CASE(key_test1)
{ {
CKey key1 = DecodeSecret(strSecret1); KeyIO keyIO(Params());
CKey key1 = keyIO.DecodeSecret(strSecret1);
BOOST_CHECK(key1.IsValid() && !key1.IsCompressed()); BOOST_CHECK(key1.IsValid() && !key1.IsCompressed());
CKey key2 = DecodeSecret(strSecret2); CKey key2 = keyIO.DecodeSecret(strSecret2);
BOOST_CHECK(key2.IsValid() && !key2.IsCompressed()); BOOST_CHECK(key2.IsValid() && !key2.IsCompressed());
CKey key1C = DecodeSecret(strSecret1C); CKey key1C = keyIO.DecodeSecret(strSecret1C);
BOOST_CHECK(key1C.IsValid() && key1C.IsCompressed()); BOOST_CHECK(key1C.IsValid() && key1C.IsCompressed());
CKey key2C = DecodeSecret(strSecret2C); CKey key2C = keyIO.DecodeSecret(strSecret2C);
BOOST_CHECK(key2C.IsValid() && key2C.IsCompressed()); BOOST_CHECK(key2C.IsValid() && key2C.IsCompressed());
CKey bad_key = DecodeSecret(strAddressBad); CKey bad_key = keyIO.DecodeSecret(strAddressBad);
BOOST_CHECK(!bad_key.IsValid()); BOOST_CHECK(!bad_key.IsValid());
CPubKey pubkey1 = key1. GetPubKey(); CPubKey pubkey1 = key1. GetPubKey();
@ -103,10 +105,10 @@ BOOST_AUTO_TEST_CASE(key_test1)
BOOST_CHECK(!key2C.VerifyPubKey(pubkey2)); BOOST_CHECK(!key2C.VerifyPubKey(pubkey2));
BOOST_CHECK(key2C.VerifyPubKey(pubkey2C)); BOOST_CHECK(key2C.VerifyPubKey(pubkey2C));
BOOST_CHECK(DecodeDestination(addr1) == CTxDestination(pubkey1.GetID())); BOOST_CHECK(keyIO.DecodeDestination(addr1) == CTxDestination(pubkey1.GetID()));
BOOST_CHECK(DecodeDestination(addr2) == CTxDestination(pubkey2.GetID())); BOOST_CHECK(keyIO.DecodeDestination(addr2) == CTxDestination(pubkey2.GetID()));
BOOST_CHECK(DecodeDestination(addr1C) == CTxDestination(pubkey1C.GetID())); BOOST_CHECK(keyIO.DecodeDestination(addr1C) == CTxDestination(pubkey1C.GetID()));
BOOST_CHECK(DecodeDestination(addr2C) == CTxDestination(pubkey2C.GetID())); BOOST_CHECK(keyIO.DecodeDestination(addr2C) == CTxDestination(pubkey2C.GetID()));
for (int n=0; n<16; n++) for (int n=0; n<16; n++)
{ {
@ -189,15 +191,16 @@ BOOST_AUTO_TEST_CASE(key_test1)
BOOST_AUTO_TEST_CASE(zc_address_test) BOOST_AUTO_TEST_CASE(zc_address_test)
{ {
KeyIO keyIO(Params());
for (size_t i = 0; i < 1000; i++) { for (size_t i = 0; i < 1000; i++) {
auto sk = SproutSpendingKey::random(); auto sk = SproutSpendingKey::random();
{ {
string sk_string = EncodeSpendingKey(sk); string sk_string = keyIO.EncodeSpendingKey(sk);
BOOST_CHECK(sk_string[0] == 'S'); BOOST_CHECK(sk_string[0] == 'S');
BOOST_CHECK(sk_string[1] == 'K'); BOOST_CHECK(sk_string[1] == 'K');
auto spendingkey2 = DecodeSpendingKey(sk_string); auto spendingkey2 = keyIO.DecodeSpendingKey(sk_string);
BOOST_CHECK(IsValidSpendingKey(spendingkey2)); BOOST_CHECK(IsValidSpendingKey(spendingkey2));
BOOST_ASSERT(boost::get<SproutSpendingKey>(&spendingkey2) != nullptr); BOOST_ASSERT(boost::get<SproutSpendingKey>(&spendingkey2) != nullptr);
auto sk2 = boost::get<SproutSpendingKey>(spendingkey2); auto sk2 = boost::get<SproutSpendingKey>(spendingkey2);
@ -206,12 +209,12 @@ BOOST_AUTO_TEST_CASE(zc_address_test)
{ {
auto addr = sk.address(); auto addr = sk.address();
std::string addr_string = EncodePaymentAddress(addr); std::string addr_string = keyIO.EncodePaymentAddress(addr);
BOOST_CHECK(addr_string[0] == 'z'); BOOST_CHECK(addr_string[0] == 'z');
BOOST_CHECK(addr_string[1] == 'c'); BOOST_CHECK(addr_string[1] == 'c');
auto paymentaddr2 = DecodePaymentAddress(addr_string); auto paymentaddr2 = keyIO.DecodePaymentAddress(addr_string);
BOOST_ASSERT(IsValidPaymentAddress(paymentaddr2)); BOOST_ASSERT(IsValidPaymentAddress(paymentaddr2));
BOOST_ASSERT(boost::get<SproutPaymentAddress>(&paymentaddr2) != nullptr); BOOST_ASSERT(boost::get<SproutPaymentAddress>(&paymentaddr2) != nullptr);
@ -228,13 +231,14 @@ BOOST_AUTO_TEST_CASE(zs_address_test)
auto m = GetTestMasterSaplingSpendingKey(); auto m = GetTestMasterSaplingSpendingKey();
KeyIO keyIO(Params());
for (uint32_t i = 0; i < 1000; i++) { for (uint32_t i = 0; i < 1000; i++) {
auto sk = m.Derive(i); auto sk = m.Derive(i);
{ {
std::string sk_string = EncodeSpendingKey(sk); std::string sk_string = keyIO.EncodeSpendingKey(sk);
BOOST_CHECK(sk_string.compare(0, 27, Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY)) == 0); BOOST_CHECK(sk_string.compare(0, 27, Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY)) == 0);
auto spendingkey2 = DecodeSpendingKey(sk_string); auto spendingkey2 = keyIO.DecodeSpendingKey(sk_string);
BOOST_CHECK(IsValidSpendingKey(spendingkey2)); BOOST_CHECK(IsValidSpendingKey(spendingkey2));
BOOST_ASSERT(boost::get<SaplingExtendedSpendingKey>(&spendingkey2) != nullptr); BOOST_ASSERT(boost::get<SaplingExtendedSpendingKey>(&spendingkey2) != nullptr);
@ -244,10 +248,10 @@ BOOST_AUTO_TEST_CASE(zs_address_test)
{ {
auto addr = sk.DefaultAddress(); auto addr = sk.DefaultAddress();
std::string addr_string = EncodePaymentAddress(addr); std::string addr_string = keyIO.EncodePaymentAddress(addr);
BOOST_CHECK(addr_string.compare(0, 15, Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS)) == 0); BOOST_CHECK(addr_string.compare(0, 15, Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS)) == 0);
auto paymentaddr2 = DecodePaymentAddress(addr_string); auto paymentaddr2 = keyIO.DecodePaymentAddress(addr_string);
BOOST_CHECK(IsValidPaymentAddress(paymentaddr2)); BOOST_CHECK(IsValidPaymentAddress(paymentaddr2));
BOOST_ASSERT(boost::get<SaplingPaymentAddress>(&paymentaddr2) != nullptr); BOOST_ASSERT(boost::get<SaplingPaymentAddress>(&paymentaddr2) != nullptr);

View File

@ -20,6 +20,8 @@
#include <boost/optional.hpp> #include <boost/optional.hpp>
#define NO_MEMO {{0xF6}}
struct SpendDescriptionInfo { struct SpendDescriptionInfo {
libzcash::SaplingExpandedSpendingKey expsk; libzcash::SaplingExpandedSpendingKey expsk;
libzcash::SaplingNote note; libzcash::SaplingNote note;
@ -118,7 +120,7 @@ public:
uint256 ovk, uint256 ovk,
libzcash::SaplingPaymentAddress to, libzcash::SaplingPaymentAddress to,
CAmount value, CAmount value,
std::array<unsigned char, ZC_MEMO_SIZE> memo = {{0xF6}}); std::array<unsigned char, ZC_MEMO_SIZE> memo = NO_MEMO);
// Throws if the anchor does not match the anchor used by // Throws if the anchor does not match the anchor used by
// previously-added Sprout inputs. // previously-added Sprout inputs.
@ -130,7 +132,7 @@ public:
void AddSproutOutput( void AddSproutOutput(
libzcash::SproutPaymentAddress to, libzcash::SproutPaymentAddress to,
CAmount value, CAmount value,
std::array<unsigned char, ZC_MEMO_SIZE> memo = {{0xF6}}); std::array<unsigned char, ZC_MEMO_SIZE> memo = NO_MEMO);
// Assumes that the value correctly corresponds to the provided UTXO. // Assumes that the value correctly corresponds to the provided UTXO.
void AddTransparentInput(COutPoint utxo, CScript scriptPubKey, CAmount value); void AddTransparentInput(COutPoint utxo, CScript scriptPubKey, CAmount value);

View File

@ -286,7 +286,8 @@ libzcash::SaplingExtendedSpendingKey GetTestMasterSaplingSpendingKey() {
} }
CKey AddTestCKeyToKeyStore(CBasicKeyStore& keyStore) { CKey AddTestCKeyToKeyStore(CBasicKeyStore& keyStore) {
CKey tsk = DecodeSecret(T_SECRET_REGTEST); KeyIO keyIO(Params());
CKey tsk = keyIO.DecodeSecret(T_SECRET_REGTEST);
keyStore.AddKey(tsk); keyStore.AddKey(tsk);
return tsk; return tsk;
} }

View File

@ -91,12 +91,13 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
builder_ = builder.get(); builder_ = builder.get();
} }
toTaddr_ = DecodeDestination(std::get<0>(recipient)); KeyIO keyIO(Params());
toTaddr_ = keyIO.DecodeDestination(std::get<0>(recipient));
isToTaddr_ = IsValidDestination(toTaddr_); isToTaddr_ = IsValidDestination(toTaddr_);
isToZaddr_ = false; isToZaddr_ = false;
if (!isToTaddr_) { if (!isToTaddr_) {
auto address = DecodePaymentAddress(std::get<0>(recipient)); auto address = keyIO.DecodePaymentAddress(std::get<0>(recipient));
if (IsValidPaymentAddress(address)) { if (IsValidPaymentAddress(address)) {
isToZaddr_ = true; isToZaddr_ = true;
toPaymentAddress_ = address; toPaymentAddress_ = address;
@ -866,6 +867,7 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
arrOutputMap.push_back(static_cast<uint64_t>(outputMap[i])); arrOutputMap.push_back(static_cast<uint64_t>(outputMap[i]));
} }
KeyIO keyIO(Params());
// !!! Payment disclosure START // !!! Payment disclosure START
unsigned char buffer[32] = {0}; unsigned char buffer[32] = {0};
@ -883,7 +885,7 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr};
paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo));
LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), keyIO.EncodePaymentAddress(zaddr));
} }
// !!! Payment disclosure END // !!! Payment disclosure END

View File

@ -192,9 +192,10 @@ CAmount AsyncRPCOperation_saplingmigration::chooseAmount(const CAmount& availabl
// Unless otherwise specified, the migration destination address is the address for Sapling account 0 // Unless otherwise specified, the migration destination address is the address for Sapling account 0
libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigrationDestAddress(const HDSeed& seed) { libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigrationDestAddress(const HDSeed& seed) {
KeyIO keyIO(Params());
if (mapArgs.count("-migrationdestaddress")) { if (mapArgs.count("-migrationdestaddress")) {
std::string migrationDestAddress = mapArgs["-migrationdestaddress"]; std::string migrationDestAddress = mapArgs["-migrationdestaddress"];
auto address = DecodePaymentAddress(migrationDestAddress); auto address = keyIO.DecodePaymentAddress(migrationDestAddress);
auto saplingAddress = boost::get<libzcash::SaplingPaymentAddress>(&address); auto saplingAddress = boost::get<libzcash::SaplingPaymentAddress>(&address);
assert(saplingAddress != nullptr); // This is checked in init.cpp assert(saplingAddress != nullptr); // This is checked in init.cpp
return *saplingAddress; return *saplingAddress;

View File

@ -85,12 +85,14 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
builder_ = builder.get(); builder_ = builder.get();
} }
fromtaddr_ = DecodeDestination(fromAddress); KeyIO keyIO(Params());
fromtaddr_ = keyIO.DecodeDestination(fromAddress);
isfromtaddr_ = IsValidDestination(fromtaddr_); isfromtaddr_ = IsValidDestination(fromtaddr_);
isfromzaddr_ = false; isfromzaddr_ = false;
if (!isfromtaddr_) { if (!isfromtaddr_) {
auto address = DecodePaymentAddress(fromAddress); auto address = keyIO.DecodePaymentAddress(fromAddress);
if (IsValidPaymentAddress(address)) { if (IsValidPaymentAddress(address)) {
// We don't need to lock on the wallet as spending key related methods are thread-safe // We don't need to lock on the wallet as spending key related methods are thread-safe
if (!boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), address)) { if (!boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), address)) {
@ -343,6 +345,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(z_outputs_total)); LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(z_outputs_total));
LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(minersFee)); LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(minersFee));
KeyIO keyIO(Params());
/** /**
* SCENARIO #0 * SCENARIO #0
@ -424,7 +427,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
auto value = r.amount; auto value = r.amount;
auto hexMemo = r.memo; auto hexMemo = r.memo;
auto addr = DecodePaymentAddress(address); auto addr = keyIO.DecodePaymentAddress(address);
assert(boost::get<libzcash::SaplingPaymentAddress>(&addr) != nullptr); assert(boost::get<libzcash::SaplingPaymentAddress>(&addr) != nullptr);
auto to = boost::get<libzcash::SaplingPaymentAddress>(addr); auto to = boost::get<libzcash::SaplingPaymentAddress>(addr);
@ -438,7 +441,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
auto outputAddress = r.address; auto outputAddress = r.address;
auto amount = r.amount; auto amount = r.amount;
auto address = DecodeDestination(outputAddress); auto address = keyIO.DecodeDestination(outputAddress);
builder_.AddTransparentOutput(address, amount); builder_.AddTransparentOutput(address, amount);
} }
@ -583,7 +586,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
std::string hexMemo = smr.memo; std::string hexMemo = smr.memo;
zOutputsDeque.pop_front(); zOutputsDeque.pop_front();
PaymentAddress pa = DecodePaymentAddress(address); PaymentAddress pa = keyIO.DecodePaymentAddress(address);
JSOutput jso = JSOutput(boost::get<libzcash::SproutPaymentAddress>(pa), value); JSOutput jso = JSOutput(boost::get<libzcash::SproutPaymentAddress>(pa), value);
if (hexMemo.size() > 0) { if (hexMemo.size() > 0) {
jso.memo = get_memo_from_hex_string(hexMemo); jso.memo = get_memo_from_hex_string(hexMemo);
@ -845,7 +848,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
assert(value==0); assert(value==0);
info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new
} else { } else {
PaymentAddress pa = DecodePaymentAddress(address); PaymentAddress pa = keyIO.DecodePaymentAddress(address);
// If we are here, we know we have no Sapling outputs. // If we are here, we know we have no Sapling outputs.
JSOutput jso = JSOutput(boost::get<libzcash::SproutPaymentAddress>(pa), value); JSOutput jso = JSOutput(boost::get<libzcash::SproutPaymentAddress>(pa), value);
if (hexMemo.size() > 0) { if (hexMemo.size() > 0) {
@ -1145,6 +1148,7 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit(
arrOutputMap.push_back(static_cast<uint64_t>(outputMap[i])); arrOutputMap.push_back(static_cast<uint64_t>(outputMap[i]));
} }
KeyIO keyIO(Params());
// !!! Payment disclosure START // !!! Payment disclosure START
unsigned char buffer[32] = {0}; unsigned char buffer[32] = {0};
@ -1162,7 +1166,7 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit(
PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr};
paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo));
LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), keyIO.EncodePaymentAddress(zaddr));
} }
// !!! Payment disclosure END // !!! Payment disclosure END
@ -1179,11 +1183,13 @@ void AsyncRPCOperation_sendmany::add_taddr_outputs_to_tx() {
CMutableTransaction rawTx(tx_); CMutableTransaction rawTx(tx_);
KeyIO keyIO(Params());
for (SendManyRecipient & r : t_outputs_) { for (SendManyRecipient & r : t_outputs_) {
std::string outputAddress = r.address; std::string outputAddress = r.address;
CAmount nAmount = r.amount; CAmount nAmount = r.amount;
CTxDestination address = DecodeDestination(outputAddress); CTxDestination address = keyIO.DecodeDestination(outputAddress);
if (!IsValidDestination(address)) { if (!IsValidDestination(address)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid output address, not a valid taddr."); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid output address, not a valid taddr.");
} }

View File

@ -74,7 +74,8 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase(
} }
// Check the destination address is valid for this network i.e. not testnet being used on mainnet // Check the destination address is valid for this network i.e. not testnet being used on mainnet
auto address = DecodePaymentAddress(toAddress); KeyIO keyIO(Params());
auto address = keyIO.DecodePaymentAddress(toAddress);
if (IsValidPaymentAddress(address)) { if (IsValidPaymentAddress(address)) {
tozaddr_ = address; tozaddr_ = address;
} else { } else {
@ -386,6 +387,8 @@ UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInf
arrOutputMap.push_back(static_cast<uint64_t>(outputMap[i])); arrOutputMap.push_back(static_cast<uint64_t>(outputMap[i]));
} }
KeyIO keyIO(Params());
// !!! Payment disclosure START // !!! Payment disclosure START
unsigned char buffer[32] = {0}; unsigned char buffer[32] = {0};
memcpy(&buffer[0], &joinSplitPrivKey_[0], 32); // private key in first half of 64 byte buffer memcpy(&buffer[0], &joinSplitPrivKey_[0], 32); // private key in first half of 64 byte buffer
@ -402,7 +405,7 @@ UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInf
PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr};
paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo));
LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), keyIO.EncodePaymentAddress(zaddr));
} }
// !!! Payment disclosure END // !!! Payment disclosure END

View File

@ -8,8 +8,9 @@
#include "util.h" #include "util.h"
std::string PaymentDisclosureInfo::ToString() const { std::string PaymentDisclosureInfo::ToString() const {
KeyIO keyIO(Params());
return strprintf("PaymentDisclosureInfo(version=%d, esk=%s, joinSplitPrivKey=<omitted>, address=%s)", return strprintf("PaymentDisclosureInfo(version=%d, esk=%s, joinSplitPrivKey=<omitted>, address=%s)",
version, esk.ToString(), EncodePaymentAddress(zaddr)); version, esk.ToString(), keyIO.EncodePaymentAddress(zaddr));
} }
std::string PaymentDisclosure::ToString() const { std::string PaymentDisclosure::ToString() const {
@ -18,8 +19,9 @@ std::string PaymentDisclosure::ToString() const {
} }
std::string PaymentDisclosurePayload::ToString() const { std::string PaymentDisclosurePayload::ToString() const {
KeyIO keyIO(Params());
return strprintf("PaymentDisclosurePayload(version=%d, esk=%s, txid=%s, js=%d, n=%d, address=%s, message=%s)", return strprintf("PaymentDisclosurePayload(version=%d, esk=%s, txid=%s, js=%d, n=%d, address=%s, message=%s)",
version, esk.ToString(), txid.ToString(), js, n, EncodePaymentAddress(zaddr), message); version, esk.ToString(), txid.ToString(), js, n, keyIO.EncodePaymentAddress(zaddr), message);
} }
PaymentDisclosure::PaymentDisclosure(const uint256 &joinSplitPubKey, const PaymentDisclosureKey &key, const PaymentDisclosureInfo &info, const std::string &message) PaymentDisclosure::PaymentDisclosure(const uint256 &joinSplitPubKey, const PaymentDisclosureKey &key, const PaymentDisclosureInfo &info, const std::string &message)

View File

@ -250,10 +250,12 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp)
errs.push_back("Payment disclosure signature does not match transaction signature"); errs.push_back("Payment disclosure signature does not match transaction signature");
} }
KeyIO keyIO(Params());
// Check the payment address is valid // Check the payment address is valid
SproutPaymentAddress zaddr = pd.payload.zaddr; SproutPaymentAddress zaddr = pd.payload.zaddr;
{ {
o.pushKV("paymentAddress", EncodePaymentAddress(zaddr)); o.pushKV("paymentAddress", keyIO.EncodePaymentAddress(zaddr));
try { try {
// Decrypt the note to get value and memo field // Decrypt the note to get value and memo field

View File

@ -116,7 +116,9 @@ UniValue importprivkey(const UniValue& params, bool fHelp)
if (params.size() > 2) if (params.size() > 2)
fRescan = params[2].get_bool(); fRescan = params[2].get_bool();
CKey key = DecodeSecret(strSecret); KeyIO keyIO(Params());
CKey key = keyIO.DecodeSecret(strSecret);
if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding"); if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
CPubKey pubkey = key.GetPubKey(); CPubKey pubkey = key.GetPubKey();
@ -128,7 +130,7 @@ UniValue importprivkey(const UniValue& params, bool fHelp)
// Don't throw error in case a key is already there // Don't throw error in case a key is already there
if (pwalletMain->HaveKey(vchAddress)) { if (pwalletMain->HaveKey(vchAddress)) {
return EncodeDestination(vchAddress); return keyIO.EncodeDestination(vchAddress);
} }
pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1; pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1;
@ -144,7 +146,7 @@ UniValue importprivkey(const UniValue& params, bool fHelp)
} }
} }
return EncodeDestination(vchAddress); return keyIO.EncodeDestination(vchAddress);
} }
void ImportAddress(const CTxDestination& dest, const string& strLabel); void ImportAddress(const CTxDestination& dest, const string& strLabel);
@ -227,7 +229,8 @@ UniValue importaddress(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
CTxDestination dest = DecodeDestination(params[0].get_str()); KeyIO keyIO(Params());
CTxDestination dest = keyIO.DecodeDestination(params[0].get_str());
if (IsValidDestination(dest)) { if (IsValidDestination(dest)) {
if (fP2SH) { if (fP2SH) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead");
@ -373,6 +376,8 @@ UniValue importwallet_impl(const UniValue& params, bool fImportZKeys)
int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg());
file.seekg(0, file.beg); file.seekg(0, file.beg);
KeyIO keyIO(Params());
pwalletMain->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI pwalletMain->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI
while (file.good()) { while (file.good()) {
pwalletMain->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100)))); pwalletMain->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100))));
@ -388,7 +393,7 @@ UniValue importwallet_impl(const UniValue& params, bool fImportZKeys)
// Let's see if the address is a valid Zcash spending key // Let's see if the address is a valid Zcash spending key
if (fImportZKeys) { if (fImportZKeys) {
auto spendingkey = DecodeSpendingKey(vstr[0]); auto spendingkey = keyIO.DecodeSpendingKey(vstr[0]);
int64_t nTime = DecodeDumpTime(vstr[1]); int64_t nTime = DecodeDumpTime(vstr[1]);
// Only include hdKeypath and seedFpStr if we have both // Only include hdKeypath and seedFpStr if we have both
boost::optional<std::string> hdKeypath = (vstr.size() > 3) ? boost::optional<std::string>(vstr[2]) : boost::none; boost::optional<std::string> hdKeypath = (vstr.size() > 3) ? boost::optional<std::string>(vstr[2]) : boost::none;
@ -409,14 +414,14 @@ UniValue importwallet_impl(const UniValue& params, bool fImportZKeys)
} }
} }
CKey key = DecodeSecret(vstr[0]); CKey key = keyIO.DecodeSecret(vstr[0]);
if (!key.IsValid()) if (!key.IsValid())
continue; continue;
CPubKey pubkey = key.GetPubKey(); CPubKey pubkey = key.GetPubKey();
assert(key.VerifyPubKey(pubkey)); assert(key.VerifyPubKey(pubkey));
CKeyID keyid = pubkey.GetID(); CKeyID keyid = pubkey.GetID();
if (pwalletMain->HaveKey(keyid)) { if (pwalletMain->HaveKey(keyid)) {
LogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid)); LogPrintf("Skipping import of %s (key already present)\n", keyIO.EncodeDestination(keyid));
continue; continue;
} }
int64_t nTime = DecodeDumpTime(vstr[1]); int64_t nTime = DecodeDumpTime(vstr[1]);
@ -434,7 +439,7 @@ UniValue importwallet_impl(const UniValue& params, bool fImportZKeys)
fLabel = true; fLabel = true;
} }
} }
LogPrintf("Importing %s...\n", EncodeDestination(keyid)); LogPrintf("Importing %s...\n", keyIO.EncodeDestination(keyid));
if (!pwalletMain->AddKeyPubKey(key, pubkey)) { if (!pwalletMain->AddKeyPubKey(key, pubkey)) {
fGood = false; fGood = false;
continue; continue;
@ -489,8 +494,10 @@ UniValue dumpprivkey(const UniValue& params, bool fHelp)
EnsureWalletIsUnlocked(); EnsureWalletIsUnlocked();
KeyIO keyIO(Params());
std::string strAddress = params[0].get_str(); std::string strAddress = params[0].get_str();
CTxDestination dest = DecodeDestination(strAddress); CTxDestination dest = keyIO.DecodeDestination(strAddress);
if (!IsValidDestination(dest)) { if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address");
} }
@ -502,7 +509,7 @@ UniValue dumpprivkey(const UniValue& params, bool fHelp)
if (!pwalletMain->GetKey(*keyID, vchSecret)) { if (!pwalletMain->GetKey(*keyID, vchSecret)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
} }
return EncodeSecret(vchSecret); return keyIO.EncodeSecret(vchSecret);
} }
@ -593,6 +600,8 @@ UniValue dumpwallet_impl(const UniValue& params, bool fDumpZKeys)
mapKeyBirth.clear(); mapKeyBirth.clear();
std::sort(vKeyBirth.begin(), vKeyBirth.end()); std::sort(vKeyBirth.begin(), vKeyBirth.end());
KeyIO keyIO(Params());
// produce output // produce output
file << strprintf("# Wallet dump created by Zcash %s (%s)\n", CLIENT_BUILD, CLIENT_DATE); file << strprintf("# Wallet dump created by Zcash %s (%s)\n", CLIENT_BUILD, CLIENT_DATE);
file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime())); file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
@ -609,15 +618,15 @@ UniValue dumpwallet_impl(const UniValue& params, bool fDumpZKeys)
for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
const CKeyID &keyid = it->second; const CKeyID &keyid = it->second;
std::string strTime = EncodeDumpTime(it->first); std::string strTime = EncodeDumpTime(it->first);
std::string strAddr = EncodeDestination(keyid); std::string strAddr = keyIO.EncodeDestination(keyid);
CKey key; CKey key;
if (pwalletMain->GetKey(keyid, key)) { if (pwalletMain->GetKey(keyid, key)) {
if (pwalletMain->mapAddressBook.count(keyid)) { if (pwalletMain->mapAddressBook.count(keyid)) {
file << strprintf("%s %s label=%s # addr=%s\n", EncodeSecret(key), strTime, EncodeDumpString(pwalletMain->mapAddressBook[keyid].name), strAddr); file << strprintf("%s %s label=%s # addr=%s\n", keyIO.EncodeSecret(key), strTime, EncodeDumpString(pwalletMain->mapAddressBook[keyid].name), strAddr);
} else if (setKeyPool.count(keyid)) { } else if (setKeyPool.count(keyid)) {
file << strprintf("%s %s reserve=1 # addr=%s\n", EncodeSecret(key), strTime, strAddr); file << strprintf("%s %s reserve=1 # addr=%s\n", keyIO.EncodeSecret(key), strTime, strAddr);
} else { } else {
file << strprintf("%s %s change=1 # addr=%s\n", EncodeSecret(key), strTime, strAddr); file << strprintf("%s %s change=1 # addr=%s\n", keyIO.EncodeSecret(key), strTime, strAddr);
} }
} }
} }
@ -633,7 +642,7 @@ UniValue dumpwallet_impl(const UniValue& params, bool fDumpZKeys)
libzcash::SproutSpendingKey key; libzcash::SproutSpendingKey key;
if (pwalletMain->GetSproutSpendingKey(addr, key)) { if (pwalletMain->GetSproutSpendingKey(addr, key)) {
std::string strTime = EncodeDumpTime(pwalletMain->mapSproutZKeyMetadata[addr].nCreateTime); std::string strTime = EncodeDumpTime(pwalletMain->mapSproutZKeyMetadata[addr].nCreateTime);
file << strprintf("%s %s # zaddr=%s\n", EncodeSpendingKey(key), strTime, EncodePaymentAddress(addr)); file << strprintf("%s %s # zaddr=%s\n", keyIO.EncodeSpendingKey(key), strTime, keyIO.EncodePaymentAddress(addr));
} }
} }
std::set<libzcash::SaplingPaymentAddress> saplingAddresses; std::set<libzcash::SaplingPaymentAddress> saplingAddresses;
@ -649,9 +658,9 @@ UniValue dumpwallet_impl(const UniValue& params, bool fDumpZKeys)
std::string strTime = EncodeDumpTime(keyMeta.nCreateTime); std::string strTime = EncodeDumpTime(keyMeta.nCreateTime);
// Keys imported with z_importkey do not have zip32 metadata // Keys imported with z_importkey do not have zip32 metadata
if (keyMeta.hdKeypath.empty() || keyMeta.seedFp.IsNull()) { if (keyMeta.hdKeypath.empty() || keyMeta.seedFp.IsNull()) {
file << strprintf("%s %s # zaddr=%s\n", EncodeSpendingKey(extsk), strTime, EncodePaymentAddress(addr)); file << strprintf("%s %s # zaddr=%s\n", keyIO.EncodeSpendingKey(extsk), strTime, keyIO.EncodePaymentAddress(addr));
} else { } else {
file << strprintf("%s %s %s %s # zaddr=%s\n", EncodeSpendingKey(extsk), strTime, keyMeta.hdKeypath, keyMeta.seedFp.GetHex(), EncodePaymentAddress(addr)); file << strprintf("%s %s %s %s # zaddr=%s\n", keyIO.EncodeSpendingKey(extsk), strTime, keyMeta.hdKeypath, keyMeta.seedFp.GetHex(), keyIO.EncodePaymentAddress(addr));
} }
} }
} }
@ -737,8 +746,9 @@ UniValue z_importkey(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
} }
KeyIO keyIO(Params());
string strSecret = params[0].get_str(); string strSecret = params[0].get_str();
auto spendingkey = DecodeSpendingKey(strSecret); auto spendingkey = keyIO.DecodeSpendingKey(strSecret);
if (!IsValidSpendingKey(spendingkey)) { if (!IsValidSpendingKey(spendingkey)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key");
} }
@ -746,7 +756,7 @@ UniValue z_importkey(const UniValue& params, bool fHelp)
auto addrInfo = boost::apply_visitor(libzcash::AddressInfoFromSpendingKey{}, spendingkey); auto addrInfo = boost::apply_visitor(libzcash::AddressInfoFromSpendingKey{}, spendingkey);
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
result.pushKV("type", addrInfo.first); result.pushKV("type", addrInfo.first);
result.pushKV("address", EncodePaymentAddress(addrInfo.second)); result.pushKV("address", keyIO.EncodePaymentAddress(addrInfo.second));
// Sapling support // Sapling support
auto addResult = boost::apply_visitor(AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus()), spendingkey); auto addResult = boost::apply_visitor(AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus()), spendingkey);
@ -831,8 +841,9 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
} }
KeyIO keyIO(Params());
string strVKey = params[0].get_str(); string strVKey = params[0].get_str();
auto viewingkey = DecodeViewingKey(strVKey); auto viewingkey = keyIO.DecodeViewingKey(strVKey);
if (!IsValidViewingKey(viewingkey)) { if (!IsValidViewingKey(viewingkey)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key");
} }
@ -840,7 +851,7 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp)
auto addrInfo = boost::apply_visitor(libzcash::AddressInfoFromViewingKey{}, viewingkey); auto addrInfo = boost::apply_visitor(libzcash::AddressInfoFromViewingKey{}, viewingkey);
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
result.pushKV("type", addrInfo.first); result.pushKV("type", addrInfo.first);
result.pushKV("address", EncodePaymentAddress(addrInfo.second)); result.pushKV("address", keyIO.EncodePaymentAddress(addrInfo.second));
auto addResult = boost::apply_visitor(AddViewingKeyToWallet(pwalletMain), viewingkey); auto addResult = boost::apply_visitor(AddViewingKeyToWallet(pwalletMain), viewingkey);
if (addResult == SpendingKeyExists) { if (addResult == SpendingKeyExists) {
@ -889,7 +900,8 @@ UniValue z_exportkey(const UniValue& params, bool fHelp)
string strAddress = params[0].get_str(); string strAddress = params[0].get_str();
auto address = DecodePaymentAddress(strAddress); KeyIO keyIO(Params());
auto address = keyIO.DecodePaymentAddress(strAddress);
if (!IsValidPaymentAddress(address)) { if (!IsValidPaymentAddress(address)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr");
} }
@ -899,7 +911,7 @@ UniValue z_exportkey(const UniValue& params, bool fHelp)
if (!sk) { if (!sk) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private zkey for this zaddr"); throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private zkey for this zaddr");
} }
return EncodeSpendingKey(sk.get()); return keyIO.EncodeSpendingKey(sk.get());
} }
UniValue z_exportviewingkey(const UniValue& params, bool fHelp) UniValue z_exportviewingkey(const UniValue& params, bool fHelp)
@ -927,14 +939,15 @@ UniValue z_exportviewingkey(const UniValue& params, bool fHelp)
string strAddress = params[0].get_str(); string strAddress = params[0].get_str();
auto address = DecodePaymentAddress(strAddress); KeyIO keyIO(Params());
auto address = keyIO.DecodePaymentAddress(strAddress);
if (!IsValidPaymentAddress(address)) { if (!IsValidPaymentAddress(address)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr");
} }
auto vk = boost::apply_visitor(GetViewingKeyForPaymentAddress(pwalletMain), address); auto vk = boost::apply_visitor(GetViewingKeyForPaymentAddress(pwalletMain), address);
if (vk) { if (vk) {
return EncodeViewingKey(vk.get()); return keyIO.EncodeViewingKey(vk.get());
} else { } else {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private key or viewing key for this zaddr"); throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold private key or viewing key for this zaddr");
} }

View File

@ -168,7 +168,8 @@ UniValue getnewaddress(const UniValue& params, bool fHelp)
pwalletMain->SetAddressBook(keyID, strAccount, "receive"); pwalletMain->SetAddressBook(keyID, strAccount, "receive");
return EncodeDestination(keyID); KeyIO keyIO(Params());
return keyIO.EncodeDestination(keyID);
} }
@ -236,7 +237,8 @@ UniValue getaccountaddress(const UniValue& params, bool fHelp)
UniValue ret(UniValue::VSTR); UniValue ret(UniValue::VSTR);
ret = EncodeDestination(GetAccountAddress(strAccount)); KeyIO keyIO(Params());
ret = keyIO.EncodeDestination(GetAccountAddress(strAccount));
return ret; return ret;
} }
@ -272,7 +274,8 @@ UniValue getrawchangeaddress(const UniValue& params, bool fHelp)
CKeyID keyID = vchPubKey.GetID(); CKeyID keyID = vchPubKey.GetID();
return EncodeDestination(keyID); KeyIO keyIO(Params());
return keyIO.EncodeDestination(keyID);
} }
@ -295,7 +298,8 @@ UniValue setaccount(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
CTxDestination dest = DecodeDestination(params[0].get_str()); KeyIO keyIO(Params());
CTxDestination dest = keyIO.DecodeDestination(params[0].get_str());
if (!IsValidDestination(dest)) { if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address");
} }
@ -342,7 +346,8 @@ UniValue getaccount(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
CTxDestination dest = DecodeDestination(params[0].get_str()); KeyIO keyIO(Params());
CTxDestination dest = keyIO.DecodeDestination(params[0].get_str());
if (!IsValidDestination(dest)) { if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address");
} }
@ -381,13 +386,14 @@ UniValue getaddressesbyaccount(const UniValue& params, bool fHelp)
string strAccount = AccountFromValue(params[0]); string strAccount = AccountFromValue(params[0]);
KeyIO keyIO(Params());
// Find all addresses that have the given account // Find all addresses that have the given account
UniValue ret(UniValue::VARR); UniValue ret(UniValue::VARR);
for (const std::pair<CTxDestination, CAddressBookData>& item : pwalletMain->mapAddressBook) { for (const std::pair<CTxDestination, CAddressBookData>& item : pwalletMain->mapAddressBook) {
const CTxDestination& dest = item.first; const CTxDestination& dest = item.first;
const std::string& strName = item.second.name; const std::string& strName = item.second.name;
if (strName == strAccount) { if (strName == strAccount) {
ret.push_back(EncodeDestination(dest)); ret.push_back(keyIO.EncodeDestination(dest));
} }
} }
return ret; return ret;
@ -455,7 +461,8 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
CTxDestination dest = DecodeDestination(params[0].get_str()); KeyIO keyIO(Params());
CTxDestination dest = keyIO.DecodeDestination(params[0].get_str());
if (!IsValidDestination(dest)) { if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address");
} }
@ -513,6 +520,7 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
KeyIO keyIO(Params());
UniValue jsonGroupings(UniValue::VARR); UniValue jsonGroupings(UniValue::VARR);
std::map<CTxDestination, CAmount> balances = pwalletMain->GetAddressBalances(); std::map<CTxDestination, CAmount> balances = pwalletMain->GetAddressBalances();
for (const std::set<CTxDestination>& grouping : pwalletMain->GetAddressGroupings()) { for (const std::set<CTxDestination>& grouping : pwalletMain->GetAddressGroupings()) {
@ -520,7 +528,7 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp)
for (const CTxDestination& address : grouping) for (const CTxDestination& address : grouping)
{ {
UniValue addressInfo(UniValue::VARR); UniValue addressInfo(UniValue::VARR);
addressInfo.push_back(EncodeDestination(address)); addressInfo.push_back(keyIO.EncodeDestination(address));
addressInfo.push_back(ValueFromAmount(balances[address])); addressInfo.push_back(ValueFromAmount(balances[address]));
{ {
if (pwalletMain->mapAddressBook.find(address) != pwalletMain->mapAddressBook.end()) { if (pwalletMain->mapAddressBook.find(address) != pwalletMain->mapAddressBook.end()) {
@ -567,7 +575,8 @@ UniValue signmessage(const UniValue& params, bool fHelp)
string strAddress = params[0].get_str(); string strAddress = params[0].get_str();
string strMessage = params[1].get_str(); string strMessage = params[1].get_str();
CTxDestination dest = DecodeDestination(strAddress); KeyIO keyIO(Params());
CTxDestination dest = keyIO.DecodeDestination(strAddress);
if (!IsValidDestination(dest)) { if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
} }
@ -621,8 +630,9 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
KeyIO keyIO(Params());
// Bitcoin address // Bitcoin address
CTxDestination dest = DecodeDestination(params[0].get_str()); CTxDestination dest = keyIO.DecodeDestination(params[0].get_str());
if (!IsValidDestination(dest)) { if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address");
} }
@ -960,8 +970,9 @@ UniValue sendfrom(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
KeyIO keyIO(Params());
std::string strAccount = AccountFromValue(params[0]); std::string strAccount = AccountFromValue(params[0]);
CTxDestination dest = DecodeDestination(params[1].get_str()); CTxDestination dest = keyIO.DecodeDestination(params[1].get_str());
if (!IsValidDestination(dest)) { if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address");
} }
@ -1053,10 +1064,11 @@ UniValue sendmany(const UniValue& params, bool fHelp)
std::set<CTxDestination> destinations; std::set<CTxDestination> destinations;
std::vector<CRecipient> vecSend; std::vector<CRecipient> vecSend;
KeyIO keyIO(Params());
CAmount totalAmount = 0; CAmount totalAmount = 0;
std::vector<std::string> keys = sendTo.getKeys(); std::vector<std::string> keys = sendTo.getKeys();
for (const std::string& name_ : keys) { for (const std::string& name_ : keys) {
CTxDestination dest = DecodeDestination(name_); CTxDestination dest = keyIO.DecodeDestination(name_);
if (!IsValidDestination(dest)) { if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Zcash address: ") + name_); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Zcash address: ") + name_);
} }
@ -1152,7 +1164,8 @@ UniValue addmultisigaddress(const UniValue& params, bool fHelp)
pwalletMain->AddCScript(inner); pwalletMain->AddCScript(inner);
pwalletMain->SetAddressBook(innerID, strAccount, "send"); pwalletMain->SetAddressBook(innerID, strAccount, "send");
return EncodeDestination(innerID); KeyIO keyIO(Params());
return keyIO.EncodeDestination(innerID);
} }
@ -1218,6 +1231,8 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts)
} }
} }
KeyIO keyIO(Params());
// Reply // Reply
UniValue ret(UniValue::VARR); UniValue ret(UniValue::VARR);
std::map<std::string, tallyitem> mapAccountTally; std::map<std::string, tallyitem> mapAccountTally;
@ -1250,7 +1265,7 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts)
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
if(fIsWatchonly) if(fIsWatchonly)
obj.pushKV("involvesWatchonly", true); obj.pushKV("involvesWatchonly", true);
obj.pushKV("address", EncodeDestination(dest)); obj.pushKV("address", keyIO.EncodeDestination(dest));
obj.pushKV("account", strAccount); obj.pushKV("account", strAccount);
obj.pushKV("amount", ValueFromAmount(nAmount)); obj.pushKV("amount", ValueFromAmount(nAmount));
obj.pushKV("amountZat", nAmount); obj.pushKV("amountZat", nAmount);
@ -1366,7 +1381,8 @@ UniValue listreceivedbyaccount(const UniValue& params, bool fHelp)
static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
{ {
if (IsValidDestination(dest)) { if (IsValidDestination(dest)) {
entry.pushKV("address", EncodeDestination(dest)); KeyIO keyIO(Params());
entry.pushKV("address", keyIO.EncodeDestination(dest));
} }
} }
@ -2440,12 +2456,13 @@ UniValue listunspent(const UniValue& params, bool fHelp)
if (params.size() > 1) if (params.size() > 1)
nMaxDepth = params[1].get_int(); nMaxDepth = params[1].get_int();
KeyIO keyIO(Params());
std::set<CTxDestination> destinations; std::set<CTxDestination> destinations;
if (params.size() > 2) { if (params.size() > 2) {
UniValue inputs = params[2].get_array(); UniValue inputs = params[2].get_array();
for (size_t idx = 0; idx < inputs.size(); idx++) { for (size_t idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx]; const UniValue& input = inputs[idx];
CTxDestination dest = DecodeDestination(input.get_str()); CTxDestination dest = keyIO.DecodeDestination(input.get_str());
if (!IsValidDestination(dest)) { if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Zcash address: ") + input.get_str()); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Zcash address: ") + input.get_str());
} }
@ -2477,7 +2494,7 @@ UniValue listunspent(const UniValue& params, bool fHelp)
entry.pushKV("generated", out.tx->IsCoinBase()); entry.pushKV("generated", out.tx->IsCoinBase());
if (fValidAddress) { if (fValidAddress) {
entry.pushKV("address", EncodeDestination(address)); entry.pushKV("address", keyIO.EncodeDestination(address));
if (pwalletMain->mapAddressBook.count(address)) if (pwalletMain->mapAddressBook.count(address))
entry.pushKV("account", pwalletMain->mapAddressBook[address].name); entry.pushKV("account", pwalletMain->mapAddressBook[address].name);
@ -2575,6 +2592,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
KeyIO keyIO(Params());
// User has supplied zaddrs to filter on // User has supplied zaddrs to filter on
if (params.size() > 3) { if (params.size() > 3) {
UniValue addresses = params[3].get_array(); UniValue addresses = params[3].get_array();
@ -2590,7 +2608,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected string"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected string");
} }
string address = o.get_str(); string address = o.get_str();
auto zaddr = DecodePaymentAddress(address); auto zaddr = keyIO.DecodePaymentAddress(address);
if (!IsValidPaymentAddress(zaddr)) { if (!IsValidPaymentAddress(zaddr)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, address is not a valid zaddr: ") + address); throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, address is not a valid zaddr: ") + address);
} }
@ -2635,7 +2653,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
obj.pushKV("confirmations", entry.confirmations); obj.pushKV("confirmations", entry.confirmations);
bool hasSproutSpendingKey = HaveSpendingKeyForPaymentAddress(pwalletMain)(entry.address); bool hasSproutSpendingKey = HaveSpendingKeyForPaymentAddress(pwalletMain)(entry.address);
obj.pushKV("spendable", hasSproutSpendingKey); obj.pushKV("spendable", hasSproutSpendingKey);
obj.pushKV("address", EncodePaymentAddress(entry.address)); obj.pushKV("address", keyIO.EncodePaymentAddress(entry.address));
obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value())));
std::string data(entry.memo.begin(), entry.memo.end()); std::string data(entry.memo.begin(), entry.memo.end());
obj.pushKV("memo", HexStr(data)); obj.pushKV("memo", HexStr(data));
@ -2652,7 +2670,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
obj.pushKV("confirmations", entry.confirmations); obj.pushKV("confirmations", entry.confirmations);
bool hasSaplingSpendingKey = HaveSpendingKeyForPaymentAddress(pwalletMain)(entry.address); bool hasSaplingSpendingKey = HaveSpendingKeyForPaymentAddress(pwalletMain)(entry.address);
obj.pushKV("spendable", hasSaplingSpendingKey); obj.pushKV("spendable", hasSaplingSpendingKey);
obj.pushKV("address", EncodePaymentAddress(entry.address)); obj.pushKV("address", keyIO.EncodePaymentAddress(entry.address));
obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); // note.value() is equivalent to plaintext.value() obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); // note.value() is equivalent to plaintext.value()
obj.pushKV("memo", HexStr(entry.memo)); obj.pushKV("memo", HexStr(entry.memo));
if (hasSaplingSpendingKey) { if (hasSaplingSpendingKey) {
@ -2914,7 +2932,8 @@ UniValue zc_raw_receive(const UniValue& params, bool fHelp)
LOCK(cs_main); LOCK(cs_main);
auto spendingkey = DecodeSpendingKey(params[0].get_str()); KeyIO keyIO(Params());
auto spendingkey = keyIO.DecodeSpendingKey(params[0].get_str());
if (!IsValidSpendingKey(spendingkey)) { if (!IsValidSpendingKey(spendingkey)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key");
} }
@ -3037,8 +3056,9 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp)
std::vector<SproutSpendingKey> keys; std::vector<SproutSpendingKey> keys;
std::vector<uint256> commitments; std::vector<uint256> commitments;
KeyIO keyIO(Params());
for (const string& name_ : inputs.getKeys()) { for (const string& name_ : inputs.getKeys()) {
auto spendingkey = DecodeSpendingKey(inputs[name_].get_str()); auto spendingkey = keyIO.DecodeSpendingKey(inputs[name_].get_str());
if (!IsValidSpendingKey(spendingkey)) { if (!IsValidSpendingKey(spendingkey)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key");
} }
@ -3086,7 +3106,7 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp)
} }
for (const string& name_ : outputs.getKeys()) { for (const string& name_ : outputs.getKeys()) {
auto addrTo = DecodePaymentAddress(name_); auto addrTo = keyIO.DecodePaymentAddress(name_);
if (!IsValidPaymentAddress(addrTo)) { if (!IsValidPaymentAddress(addrTo)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid recipient address."); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid recipient address.");
} }
@ -3206,10 +3226,11 @@ UniValue zc_raw_keygen(const UniValue& params, bool fHelp)
auto addr = k.address(); auto addr = k.address();
auto viewing_key = k.viewing_key(); auto viewing_key = k.viewing_key();
KeyIO keyIO(Params());
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
result.pushKV("zcaddress", EncodePaymentAddress(addr)); result.pushKV("zcaddress", keyIO.EncodePaymentAddress(addr));
result.pushKV("zcsecretkey", EncodeSpendingKey(k)); result.pushKV("zcsecretkey", keyIO.EncodeSpendingKey(k));
result.pushKV("zcviewingkey", EncodeViewingKey(viewing_key)); result.pushKV("zcviewingkey", keyIO.EncodeViewingKey(viewing_key));
return result; return result;
} }
@ -3246,10 +3267,11 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp)
addrType = params[0].get_str(); addrType = params[0].get_str();
} }
KeyIO keyIO(Params());
if (addrType == ADDR_TYPE_SPROUT) { if (addrType == ADDR_TYPE_SPROUT) {
return EncodePaymentAddress(pwalletMain->GenerateNewSproutZKey()); return keyIO.EncodePaymentAddress(pwalletMain->GenerateNewSproutZKey());
} else if (addrType == ADDR_TYPE_SAPLING) { } else if (addrType == ADDR_TYPE_SAPLING) {
return EncodePaymentAddress(pwalletMain->GenerateNewSaplingZKey()); return keyIO.EncodePaymentAddress(pwalletMain->GenerateNewSaplingZKey());
} else { } else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid address type"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid address type");
} }
@ -3284,13 +3306,14 @@ UniValue z_listaddresses(const UniValue& params, bool fHelp)
fIncludeWatchonly = params[0].get_bool(); fIncludeWatchonly = params[0].get_bool();
} }
KeyIO keyIO(Params());
UniValue ret(UniValue::VARR); UniValue ret(UniValue::VARR);
{ {
std::set<libzcash::SproutPaymentAddress> addresses; std::set<libzcash::SproutPaymentAddress> addresses;
pwalletMain->GetSproutPaymentAddresses(addresses); pwalletMain->GetSproutPaymentAddresses(addresses);
for (auto addr : addresses) { for (auto addr : addresses) {
if (fIncludeWatchonly || HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { if (fIncludeWatchonly || HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) {
ret.push_back(EncodePaymentAddress(addr)); ret.push_back(keyIO.EncodePaymentAddress(addr));
} }
} }
} }
@ -3299,7 +3322,7 @@ UniValue z_listaddresses(const UniValue& params, bool fHelp)
pwalletMain->GetSaplingPaymentAddresses(addresses); pwalletMain->GetSaplingPaymentAddresses(addresses);
for (auto addr : addresses) { for (auto addr : addresses) {
if (fIncludeWatchonly || HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) { if (fIncludeWatchonly || HaveSpendingKeyForPaymentAddress(pwalletMain)(addr)) {
ret.push_back(EncodePaymentAddress(addr)); ret.push_back(keyIO.EncodePaymentAddress(addr));
} }
} }
} }
@ -3311,8 +3334,9 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1, bool ign
vector<COutput> vecOutputs; vector<COutput> vecOutputs;
CAmount balance = 0; CAmount balance = 0;
KeyIO keyIO(Params());
if (transparentAddress.length() > 0) { if (transparentAddress.length() > 0) {
CTxDestination taddr = DecodeDestination(transparentAddress); CTxDestination taddr = keyIO.DecodeDestination(transparentAddress);
if (!IsValidDestination(taddr)) { if (!IsValidDestination(taddr)) {
throw std::runtime_error("invalid transparent address"); throw std::runtime_error("invalid transparent address");
} }
@ -3427,7 +3451,8 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
// Check that the from address is valid. // Check that the from address is valid.
auto fromaddress = params[0].get_str(); auto fromaddress = params[0].get_str();
auto zaddr = DecodePaymentAddress(fromaddress); KeyIO keyIO(Params());
auto zaddr = keyIO.DecodePaymentAddress(fromaddress);
if (!IsValidPaymentAddress(zaddr)) { if (!IsValidPaymentAddress(zaddr)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr."); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr.");
} }
@ -3530,13 +3555,14 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
} }
KeyIO keyIO(Params());
// Check that the from address is valid. // Check that the from address is valid.
auto fromaddress = params[0].get_str(); auto fromaddress = params[0].get_str();
bool fromTaddr = false; bool fromTaddr = false;
CTxDestination taddr = DecodeDestination(fromaddress); CTxDestination taddr = keyIO.DecodeDestination(fromaddress);
fromTaddr = IsValidDestination(taddr); fromTaddr = IsValidDestination(taddr);
if (!fromTaddr) { if (!fromTaddr) {
auto res = DecodePaymentAddress(fromaddress); auto res = keyIO.DecodePaymentAddress(fromaddress);
if (!IsValidPaymentAddress(res)) { if (!IsValidPaymentAddress(res)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr.");
} }
@ -3705,6 +3731,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
} }
}; };
KeyIO keyIO(Params());
// Sprout spends // Sprout spends
for (size_t i = 0; i < wtx.vJoinSplit.size(); ++i) { for (size_t i = 0; i < wtx.vJoinSplit.size(); ++i) {
for (size_t j = 0; j < wtx.vJoinSplit[i].nullifiers.size(); ++j) { for (size_t j = 0; j < wtx.vJoinSplit[i].nullifiers.size(); ++j) {
@ -3729,7 +3756,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
entry.pushKV("txidPrev", jsop.hash.GetHex()); entry.pushKV("txidPrev", jsop.hash.GetHex());
entry.pushKV("jsPrev", (int)jsop.js); entry.pushKV("jsPrev", (int)jsop.js);
entry.pushKV("jsOutputPrev", (int)jsop.n); entry.pushKV("jsOutputPrev", (int)jsop.n);
entry.pushKV("address", EncodePaymentAddress(pa)); entry.pushKV("address", keyIO.EncodePaymentAddress(pa));
entry.pushKV("value", ValueFromAmount(notePt.value())); entry.pushKV("value", ValueFromAmount(notePt.value()));
entry.pushKV("valueZat", notePt.value()); entry.pushKV("valueZat", notePt.value());
spends.push_back(entry); spends.push_back(entry);
@ -3749,7 +3776,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
entry.pushKV("type", ADDR_TYPE_SPROUT); entry.pushKV("type", ADDR_TYPE_SPROUT);
entry.pushKV("js", (int)jsop.js); entry.pushKV("js", (int)jsop.js);
entry.pushKV("jsOutput", (int)jsop.n); entry.pushKV("jsOutput", (int)jsop.n);
entry.pushKV("address", EncodePaymentAddress(pa)); entry.pushKV("address", keyIO.EncodePaymentAddress(pa));
entry.pushKV("value", ValueFromAmount(notePt.value())); entry.pushKV("value", ValueFromAmount(notePt.value()));
entry.pushKV("valueZat", notePt.value()); entry.pushKV("valueZat", notePt.value());
addMemo(entry, memo); addMemo(entry, memo);
@ -3792,7 +3819,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
entry.pushKV("spend", (int)i); entry.pushKV("spend", (int)i);
entry.pushKV("txidPrev", op.hash.GetHex()); entry.pushKV("txidPrev", op.hash.GetHex());
entry.pushKV("outputPrev", (int)op.n); entry.pushKV("outputPrev", (int)op.n);
entry.pushKV("address", EncodePaymentAddress(pa)); entry.pushKV("address", keyIO.EncodePaymentAddress(pa));
entry.pushKV("value", ValueFromAmount(notePt.value())); entry.pushKV("value", ValueFromAmount(notePt.value()));
entry.pushKV("valueZat", notePt.value()); entry.pushKV("valueZat", notePt.value());
spends.push_back(entry); spends.push_back(entry);
@ -3831,7 +3858,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp)
entry.pushKV("type", ADDR_TYPE_SAPLING); entry.pushKV("type", ADDR_TYPE_SAPLING);
entry.pushKV("output", (int)op.n); entry.pushKV("output", (int)op.n);
entry.pushKV("outgoing", isOutgoing); entry.pushKV("outgoing", isOutgoing);
entry.pushKV("address", EncodePaymentAddress(pa)); entry.pushKV("address", keyIO.EncodePaymentAddress(pa));
entry.pushKV("value", ValueFromAmount(notePt.value())); entry.pushKV("value", ValueFromAmount(notePt.value()));
entry.pushKV("valueZat", notePt.value()); entry.pushKV("valueZat", notePt.value());
addMemo(entry, memo); addMemo(entry, memo);
@ -3997,10 +4024,11 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
auto fromaddress = params[0].get_str(); auto fromaddress = params[0].get_str();
bool fromTaddr = false; bool fromTaddr = false;
bool fromSapling = false; bool fromSapling = false;
CTxDestination taddr = DecodeDestination(fromaddress); KeyIO keyIO(Params());
CTxDestination taddr = keyIO.DecodeDestination(fromaddress);
fromTaddr = IsValidDestination(taddr); fromTaddr = IsValidDestination(taddr);
if (!fromTaddr) { if (!fromTaddr) {
auto res = DecodePaymentAddress(fromaddress); auto res = keyIO.DecodePaymentAddress(fromaddress);
if (!IsValidPaymentAddress(res)) { if (!IsValidPaymentAddress(res)) {
// invalid // invalid
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr.");
@ -4049,9 +4077,9 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
string address = find_value(o, "address").get_str(); string address = find_value(o, "address").get_str();
bool isZaddr = false; bool isZaddr = false;
CTxDestination taddr = DecodeDestination(address); CTxDestination taddr = keyIO.DecodeDestination(address);
if (!IsValidDestination(taddr)) { if (!IsValidDestination(taddr)) {
auto res = DecodePaymentAddress(address); auto res = keyIO.DecodePaymentAddress(address);
if (IsValidPaymentAddress(res)) { if (IsValidPaymentAddress(res)) {
isZaddr = true; isZaddr = true;
@ -4153,7 +4181,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
size_t txsize = 0; size_t txsize = 0;
for (int i = 0; i < zaddrRecipients.size(); i++) { for (int i = 0; i < zaddrRecipients.size(); i++) {
auto address = zaddrRecipients[i].address; auto address = zaddrRecipients[i].address;
auto res = DecodePaymentAddress(address); auto res = keyIO.DecodePaymentAddress(address);
bool toSapling = boost::get<libzcash::SaplingPaymentAddress>(&res) != nullptr; bool toSapling = boost::get<libzcash::SaplingPaymentAddress>(&res) != nullptr;
if (toSapling) { if (toSapling) {
mtx.vShieldedOutput.push_back(OutputDescription()); mtx.vShieldedOutput.push_back(OutputDescription());
@ -4302,7 +4330,8 @@ UniValue z_getmigrationstatus(const UniValue& params, bool fHelp) {
// parameter is not set and no default address has yet been generated. // parameter is not set and no default address has yet been generated.
// Note: The following function may return the default address even if it has not been added to the wallet // Note: The following function may return the default address even if it has not been added to the wallet
auto destinationAddress = AsyncRPCOperation_saplingmigration::getMigrationDestAddress(pwalletMain->GetHDSeedForRPC()); auto destinationAddress = AsyncRPCOperation_saplingmigration::getMigrationDestAddress(pwalletMain->GetHDSeedForRPC());
migrationStatus.pushKV("destination_address", EncodePaymentAddress(destinationAddress)); KeyIO keyIO(Params());
migrationStatus.pushKV("destination_address", keyIO.EncodePaymentAddress(destinationAddress));
// The values of "unmigrated_amount" and "migrated_amount" MUST take into // The values of "unmigrated_amount" and "migrated_amount" MUST take into
// account failed transactions, that were not mined within their expiration // account failed transactions, that were not mined within their expiration
// height. // height.
@ -4431,9 +4460,10 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp)
// Validate the from address // Validate the from address
auto fromaddress = params[0].get_str(); auto fromaddress = params[0].get_str();
bool isFromWildcard = fromaddress == "*"; bool isFromWildcard = fromaddress == "*";
KeyIO keyIO(Params());
CTxDestination taddr; CTxDestination taddr;
if (!isFromWildcard) { if (!isFromWildcard) {
taddr = DecodeDestination(fromaddress); taddr = keyIO.DecodeDestination(fromaddress);
if (!IsValidDestination(taddr)) { if (!IsValidDestination(taddr)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or \"*\"."); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or \"*\".");
} }
@ -4441,7 +4471,7 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp)
// Validate the destination address // Validate the destination address
auto destaddress = params[1].get_str(); auto destaddress = params[1].get_str();
if (!IsValidPaymentAddressString(destaddress)) { if (!keyIO.IsValidPaymentAddressString(destaddress)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress );
} }
@ -4449,7 +4479,7 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp)
const bool canopyActive = Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_CANOPY); const bool canopyActive = Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_CANOPY);
if (canopyActive) { if (canopyActive) {
auto decodeAddr = DecodePaymentAddress(destaddress); auto decodeAddr = keyIO.DecodePaymentAddress(destaddress);
bool isToSproutZaddr = (boost::get<libzcash::SproutPaymentAddress>(&decodeAddr) != nullptr); bool isToSproutZaddr = (boost::get<libzcash::SproutPaymentAddress>(&decodeAddr) != nullptr);
if (isToSproutZaddr) { if (isToSproutZaddr) {
@ -4678,6 +4708,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
bool isFromNonSprout = false; bool isFromNonSprout = false;
KeyIO keyIO(Params());
// Sources // Sources
for (const UniValue& o : addresses.getValues()) { for (const UniValue& o : addresses.getValues()) {
if (!o.isStr()) if (!o.isStr())
@ -4694,12 +4725,12 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
useAnySapling = true; useAnySapling = true;
isFromNonSprout = true; isFromNonSprout = true;
} else { } else {
CTxDestination taddr = DecodeDestination(address); CTxDestination taddr = keyIO.DecodeDestination(address);
if (IsValidDestination(taddr)) { if (IsValidDestination(taddr)) {
taddrs.insert(taddr); taddrs.insert(taddr);
isFromNonSprout = true; isFromNonSprout = true;
} else { } else {
auto zaddr = DecodePaymentAddress(address); auto zaddr = keyIO.DecodePaymentAddress(address);
if (IsValidPaymentAddress(zaddr)) { if (IsValidPaymentAddress(zaddr)) {
zaddrs.insert(zaddr); zaddrs.insert(zaddr);
if (boost::get<libzcash::SaplingPaymentAddress>(&zaddr) != nullptr) { if (boost::get<libzcash::SaplingPaymentAddress>(&zaddr) != nullptr) {
@ -4732,9 +4763,9 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
auto destaddress = params[1].get_str(); auto destaddress = params[1].get_str();
bool isToSproutZaddr = false; bool isToSproutZaddr = false;
bool isToSaplingZaddr = false; bool isToSaplingZaddr = false;
CTxDestination taddr = DecodeDestination(destaddress); CTxDestination taddr = keyIO.DecodeDestination(destaddress);
if (!IsValidDestination(taddr)) { if (!IsValidDestination(taddr)) {
auto decodeAddr = DecodePaymentAddress(destaddress); auto decodeAddr = keyIO.DecodePaymentAddress(destaddress);
if (IsValidPaymentAddress(decodeAddr)) { if (IsValidPaymentAddress(decodeAddr)) {
if (boost::get<libzcash::SaplingPaymentAddress>(&decodeAddr) != nullptr) { if (boost::get<libzcash::SaplingPaymentAddress>(&decodeAddr) != nullptr) {
isToSaplingZaddr = true; isToSaplingZaddr = true;

View File

@ -68,18 +68,19 @@ BOOST_AUTO_TEST_CASE(rpc_addmultisig)
// new, compressed: // new, compressed:
const char address2Hex[] = "0388c2037017c62240b6b72ac1a2a5f94da790596ebd06177c8572752922165cb4"; const char address2Hex[] = "0388c2037017c62240b6b72ac1a2a5f94da790596ebd06177c8572752922165cb4";
KeyIO keyIO(Params());
UniValue v; UniValue v;
CTxDestination address; CTxDestination address;
BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex), false)); BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex), false));
address = DecodeDestination(v.get_str()); address = keyIO.DecodeDestination(v.get_str());
BOOST_CHECK(IsValidDestination(address) && IsScriptDestination(address)); BOOST_CHECK(IsValidDestination(address) && IsScriptDestination(address));
BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex, address2Hex), false)); BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex, address2Hex), false));
address = DecodeDestination(v.get_str()); address = keyIO.DecodeDestination(v.get_str());
BOOST_CHECK(IsValidDestination(address) && IsScriptDestination(address)); BOOST_CHECK(IsValidDestination(address) && IsScriptDestination(address));
BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(2, address1Hex, address2Hex), false)); BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(2, address1Hex, address2Hex), false));
address = DecodeDestination(v.get_str()); address = keyIO.DecodeDestination(v.get_str());
BOOST_CHECK(IsValidDestination(address) && IsScriptDestination(address)); BOOST_CHECK(IsValidDestination(address) && IsScriptDestination(address));
BOOST_CHECK_THROW(addmultisig(createArgs(0), false), runtime_error); BOOST_CHECK_THROW(addmultisig(createArgs(0), false), runtime_error);
@ -122,9 +123,10 @@ BOOST_AUTO_TEST_CASE(rpc_wallet)
/********************************* /*********************************
* setaccount * setaccount
*********************************/ *********************************/
BOOST_CHECK_NO_THROW(CallRPC("setaccount " + EncodeDestination(setaccountDemoAddress) + " \"\"")); KeyIO keyIO(Params());
BOOST_CHECK_NO_THROW(CallRPC("setaccount " + keyIO.EncodeDestination(setaccountDemoAddress) + " \"\""));
/* Accounts are disabled */ /* Accounts are disabled */
BOOST_CHECK_THROW(CallRPC("setaccount " + EncodeDestination(setaccountDemoAddress) + " nullaccount"), runtime_error); BOOST_CHECK_THROW(CallRPC("setaccount " + keyIO.EncodeDestination(setaccountDemoAddress) + " nullaccount"), runtime_error);
/* t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1GpgV is not owned by the test wallet. */ /* t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1GpgV is not owned by the test wallet. */
BOOST_CHECK_THROW(CallRPC("setaccount t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1GpgV nullaccount"), runtime_error); BOOST_CHECK_THROW(CallRPC("setaccount t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1GpgV nullaccount"), runtime_error);
BOOST_CHECK_THROW(CallRPC("setaccount"), runtime_error); BOOST_CHECK_THROW(CallRPC("setaccount"), runtime_error);
@ -136,7 +138,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet)
* getbalance * getbalance
*********************************/ *********************************/
BOOST_CHECK_NO_THROW(CallRPC("getbalance")); BOOST_CHECK_NO_THROW(CallRPC("getbalance"));
BOOST_CHECK_THROW(CallRPC("getbalance " + EncodeDestination(demoAddress)), runtime_error); BOOST_CHECK_THROW(CallRPC("getbalance " + keyIO.EncodeDestination(demoAddress)), runtime_error);
/********************************* /*********************************
* listunspent * listunspent
@ -178,10 +180,10 @@ BOOST_AUTO_TEST_CASE(rpc_wallet)
* listtransactions * listtransactions
*********************************/ *********************************/
BOOST_CHECK_NO_THROW(CallRPC("listtransactions")); BOOST_CHECK_NO_THROW(CallRPC("listtransactions"));
BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + EncodeDestination(demoAddress))); BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + keyIO.EncodeDestination(demoAddress)));
BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + EncodeDestination(demoAddress) + " 20")); BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + keyIO.EncodeDestination(demoAddress) + " 20"));
BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + EncodeDestination(demoAddress) + " 20 0")); BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + keyIO.EncodeDestination(demoAddress) + " 20 0"));
BOOST_CHECK_THROW(CallRPC("listtransactions " + EncodeDestination(demoAddress) + " not_int"), runtime_error); BOOST_CHECK_THROW(CallRPC("listtransactions " + keyIO.EncodeDestination(demoAddress) + " not_int"), runtime_error);
/********************************* /*********************************
* listlockunspent * listlockunspent
@ -218,33 +220,33 @@ BOOST_AUTO_TEST_CASE(rpc_wallet)
/* Accounts are deprecated */ /* Accounts are deprecated */
BOOST_CHECK_THROW(CallRPC("getaccountaddress accountThatDoesntExists"), runtime_error); BOOST_CHECK_THROW(CallRPC("getaccountaddress accountThatDoesntExists"), runtime_error);
BOOST_CHECK_NO_THROW(retValue = CallRPC("getaccountaddress " + strAccount)); BOOST_CHECK_NO_THROW(retValue = CallRPC("getaccountaddress " + strAccount));
BOOST_CHECK(DecodeDestination(retValue.get_str()) == demoAddress); BOOST_CHECK(keyIO.DecodeDestination(retValue.get_str()) == demoAddress);
/********************************* /*********************************
* getaccount * getaccount
*********************************/ *********************************/
BOOST_CHECK_THROW(CallRPC("getaccount"), runtime_error); BOOST_CHECK_THROW(CallRPC("getaccount"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC("getaccount " + EncodeDestination(demoAddress))); BOOST_CHECK_NO_THROW(CallRPC("getaccount " + keyIO.EncodeDestination(demoAddress)));
/********************************* /*********************************
* signmessage + verifymessage * signmessage + verifymessage
*********************************/ *********************************/
BOOST_CHECK_NO_THROW(retValue = CallRPC("signmessage " + EncodeDestination(demoAddress) + " mymessage")); BOOST_CHECK_NO_THROW(retValue = CallRPC("signmessage " + keyIO.EncodeDestination(demoAddress) + " mymessage"));
BOOST_CHECK_THROW(CallRPC("signmessage"), runtime_error); BOOST_CHECK_THROW(CallRPC("signmessage"), runtime_error);
/* Should throw error because this address is not loaded in the wallet */ /* Should throw error because this address is not loaded in the wallet */
BOOST_CHECK_THROW(CallRPC("signmessage t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe mymessage"), runtime_error); BOOST_CHECK_THROW(CallRPC("signmessage t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe mymessage"), runtime_error);
/* missing arguments */ /* missing arguments */
BOOST_CHECK_THROW(CallRPC("verifymessage " + EncodeDestination(demoAddress)), runtime_error); BOOST_CHECK_THROW(CallRPC("verifymessage " + keyIO.EncodeDestination(demoAddress)), runtime_error);
BOOST_CHECK_THROW(CallRPC("verifymessage " + EncodeDestination(demoAddress) + " " + retValue.get_str()), runtime_error); BOOST_CHECK_THROW(CallRPC("verifymessage " + keyIO.EncodeDestination(demoAddress) + " " + retValue.get_str()), runtime_error);
/* Illegal address */ /* Illegal address */
BOOST_CHECK_THROW(CallRPC("verifymessage t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1Gpg " + retValue.get_str() + " mymessage"), runtime_error); BOOST_CHECK_THROW(CallRPC("verifymessage t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1Gpg " + retValue.get_str() + " mymessage"), runtime_error);
/* wrong address */ /* wrong address */
BOOST_CHECK(CallRPC("verifymessage t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1GpgV " + retValue.get_str() + " mymessage").get_bool() == false); BOOST_CHECK(CallRPC("verifymessage t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1GpgV " + retValue.get_str() + " mymessage").get_bool() == false);
/* Correct address and signature but wrong message */ /* Correct address and signature but wrong message */
BOOST_CHECK(CallRPC("verifymessage " + EncodeDestination(demoAddress) + " " + retValue.get_str() + " wrongmessage").get_bool() == false); BOOST_CHECK(CallRPC("verifymessage " + keyIO.EncodeDestination(demoAddress) + " " + retValue.get_str() + " wrongmessage").get_bool() == false);
/* Correct address, message and signature*/ /* Correct address, message and signature*/
BOOST_CHECK(CallRPC("verifymessage " + EncodeDestination(demoAddress) + " " + retValue.get_str() + " mymessage").get_bool() == true); BOOST_CHECK(CallRPC("verifymessage " + keyIO.EncodeDestination(demoAddress) + " " + retValue.get_str() + " mymessage").get_bool() == true);
/********************************* /*********************************
* getaddressesbyaccount * getaddressesbyaccount
@ -255,7 +257,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet)
BOOST_CHECK_EQUAL(4, arr.size()); BOOST_CHECK_EQUAL(4, arr.size());
bool notFound = true; bool notFound = true;
for (auto a : arr.getValues()) { for (auto a : arr.getValues()) {
notFound &= DecodeDestination(a.get_str()) != demoAddress; notFound &= keyIO.DecodeDestination(a.get_str()) != demoAddress;
} }
BOOST_CHECK(!notFound); BOOST_CHECK(!notFound);
@ -275,36 +277,68 @@ BOOST_AUTO_TEST_CASE(rpc_wallet)
UniValue obj = retValue.get_obj(); UniValue obj = retValue.get_obj();
BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 10.0); BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 10.0);
BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 2.5); BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 2.5);
BOOST_CHECK(!obj.exists("fundingstreams"));
BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 653599")); // Blossom activation - 1 BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 653599")); // Blossom activation - 1
obj = retValue.get_obj(); obj = retValue.get_obj();
BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 10.0); BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 10.0);
BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 2.5); BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 2.5);
BOOST_CHECK(!obj.exists("fundingstreams"));
BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 653600")); // Blossom activation BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 653600")); // Blossom activation
obj = retValue.get_obj(); obj = retValue.get_obj();
BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 5.0); BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 5.0);
BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 1.25); BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 1.25);
BOOST_CHECK(!obj.exists("fundingstreams"));
BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 1046399")); BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 1046399"));
obj = retValue.get_obj(); obj = retValue.get_obj();
BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 5.0); BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 5.0);
BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 1.25); BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 1.25);
BOOST_CHECK(!obj.exists("fundingstreams"));
auto check_funding_streams = [](UniValue obj, std::vector<std::string> recipients, std::vector<double> amounts) {
size_t n = recipients.size();
BOOST_REQUIRE_EQUAL(amounts.size(), n);
UniValue fundingstreams = find_value(obj, "fundingstreams");
BOOST_CHECK_EQUAL(fundingstreams.size(), n);
if (fundingstreams.size() != n) return;
for (int i = 0; i < n; i++) {
UniValue fsobj = fundingstreams[i];
BOOST_CHECK_EQUAL(find_value(fsobj, "recipient").get_str(), recipients[i]);
BOOST_CHECK_EQUAL(find_value(fsobj, "specification").get_str(), "https://zips.z.cash/zip-0214");
BOOST_CHECK_EQUAL(find_value(fsobj, "value").get_real(), amounts[i]);
}
};
bool canopyEnabled =
Params().GetConsensus().vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight != Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
// slow start + blossom activation + (pre blossom halving - blossom activation) * 2 // slow start + blossom activation + (pre blossom halving - blossom activation) * 2
BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 1046400")); BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 1046400"));
obj = retValue.get_obj(); obj = retValue.get_obj();
BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 3.125); BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), canopyEnabled ? 2.5 : 3.125);
BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 0.0); BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 0.0);
if (canopyEnabled) {
check_funding_streams(obj, {"Electric Coin Company", "Zcash Foundation", "Major Grants" },
{ 0.21875, 0.15625, 0.25 });
}
BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 2726399")); BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 2726399"));
obj = retValue.get_obj(); obj = retValue.get_obj();
BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 3.125); BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), canopyEnabled ? 2.5 : 3.125);
BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 0.0); BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 0.0);
if (canopyEnabled) {
check_funding_streams(obj, {"Electric Coin Company", "Zcash Foundation", "Major Grants" },
{ 0.21875, 0.15625, 0.25 });
}
BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 2726400")); BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 2726400"));
obj = retValue.get_obj(); obj = retValue.get_obj();
BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 1.5625); BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 1.5625);
BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 0.0); BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 0.0);
BOOST_CHECK(find_value(obj, "fundingstreams").empty());
/* /*
* getblock * getblock
@ -493,8 +527,9 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_exportwallet)
libzcash::SproutSpendingKey key; libzcash::SproutSpendingKey key;
BOOST_CHECK(pwalletMain->GetSproutSpendingKey(addr, key)); BOOST_CHECK(pwalletMain->GetSproutSpendingKey(addr, key));
std::string s1 = EncodePaymentAddress(addr); KeyIO keyIO(Params());
std::string s2 = EncodeSpendingKey(key); std::string s1 = keyIO.EncodePaymentAddress(addr);
std::string s2 = keyIO.EncodeSpendingKey(key);
// There's no way to really delete a private key so we will read in the // There's no way to really delete a private key so we will read in the
// exported wallet file and search for the spending key and payment address. // exported wallet file and search for the spending key and payment address.
@ -534,11 +569,12 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet)
// error if too many args // error if too many args
BOOST_CHECK_THROW(CallRPC("z_importwallet toomany args"), runtime_error); BOOST_CHECK_THROW(CallRPC("z_importwallet toomany args"), runtime_error);
KeyIO keyIO(Params());
// create a random key locally // create a random key locally
auto testSpendingKey = libzcash::SproutSpendingKey::random(); auto testSpendingKey = libzcash::SproutSpendingKey::random();
auto testPaymentAddress = testSpendingKey.address(); auto testPaymentAddress = testSpendingKey.address();
std::string testAddr = EncodePaymentAddress(testPaymentAddress); std::string testAddr = keyIO.EncodePaymentAddress(testPaymentAddress);
std::string testKey = EncodeSpendingKey(testSpendingKey); std::string testKey = keyIO.EncodeSpendingKey(testSpendingKey);
// create test data using the random key // create test data using the random key
std::string format_str = "# Wallet dump created by Zcash v0.11.2.0.z8-9155cc6-dirty (2016-08-11 11:37:00 -0700)\n" std::string format_str = "# Wallet dump created by Zcash v0.11.2.0.z8-9155cc6-dirty (2016-08-11 11:37:00 -0700)\n"
@ -576,7 +612,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet)
BOOST_CHECK(addrs.size()==1); BOOST_CHECK(addrs.size()==1);
// check that we have the spending key for the address // check that we have the spending key for the address
auto address = DecodePaymentAddress(testAddr); auto address = keyIO.DecodePaymentAddress(testAddr);
BOOST_CHECK(IsValidPaymentAddress(address)); BOOST_CHECK(IsValidPaymentAddress(address));
BOOST_ASSERT(boost::get<libzcash::SproutPaymentAddress>(&address) != nullptr); BOOST_ASSERT(boost::get<libzcash::SproutPaymentAddress>(&address) != nullptr);
auto addr = boost::get<libzcash::SproutPaymentAddress>(address); auto addr = boost::get<libzcash::SproutPaymentAddress>(address);
@ -585,7 +621,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet)
// Verify the spending key is the same as the test data // Verify the spending key is the same as the test data
libzcash::SproutSpendingKey k; libzcash::SproutSpendingKey k;
BOOST_CHECK(pwalletMain->GetSproutSpendingKey(addr, k)); BOOST_CHECK(pwalletMain->GetSproutSpendingKey(addr, k));
BOOST_CHECK_EQUAL(testKey, EncodeSpendingKey(k)); BOOST_CHECK_EQUAL(testKey, keyIO.EncodeSpendingKey(k));
} }
@ -608,8 +644,9 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport)
BOOST_CHECK_THROW(CallRPC("z_exportkey toomany args"), runtime_error); BOOST_CHECK_THROW(CallRPC("z_exportkey toomany args"), runtime_error);
// error if invalid args // error if invalid args
KeyIO keyIO(Params());
auto sk = libzcash::SproutSpendingKey::random(); auto sk = libzcash::SproutSpendingKey::random();
std::string prefix = std::string("z_importkey ") + EncodeSpendingKey(sk) + " yes "; std::string prefix = std::string("z_importkey ") + keyIO.EncodeSpendingKey(sk) + " yes ";
BOOST_CHECK_THROW(CallRPC(prefix + "-1"), runtime_error); BOOST_CHECK_THROW(CallRPC(prefix + "-1"), runtime_error);
BOOST_CHECK_THROW(CallRPC(prefix + "2147483647"), runtime_error); // allowed, but > height of active chain tip BOOST_CHECK_THROW(CallRPC(prefix + "2147483647"), runtime_error); // allowed, but > height of active chain tip
BOOST_CHECK_THROW(CallRPC(prefix + "2147483648"), runtime_error); // not allowed, > int32 used for nHeight BOOST_CHECK_THROW(CallRPC(prefix + "2147483648"), runtime_error); // not allowed, > int32 used for nHeight
@ -630,8 +667,8 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport)
// create a random Sprout key locally // create a random Sprout key locally
auto testSpendingKey = libzcash::SproutSpendingKey::random(); auto testSpendingKey = libzcash::SproutSpendingKey::random();
auto testPaymentAddress = testSpendingKey.address(); auto testPaymentAddress = testSpendingKey.address();
std::string testAddr = EncodePaymentAddress(testPaymentAddress); std::string testAddr = keyIO.EncodePaymentAddress(testPaymentAddress);
std::string testKey = EncodeSpendingKey(testSpendingKey); std::string testKey = keyIO.EncodeSpendingKey(testSpendingKey);
BOOST_CHECK_NO_THROW(CallRPC(string("z_importkey ") + testKey)); BOOST_CHECK_NO_THROW(CallRPC(string("z_importkey ") + testKey));
BOOST_CHECK_NO_THROW(retValue = CallRPC(string("z_exportkey ") + testAddr)); BOOST_CHECK_NO_THROW(retValue = CallRPC(string("z_exportkey ") + testAddr));
BOOST_CHECK_EQUAL(retValue.get_str(), testKey); BOOST_CHECK_EQUAL(retValue.get_str(), testKey);
@ -639,8 +676,8 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport)
// create a random Sapling key locally // create a random Sapling key locally
auto testSaplingSpendingKey = m.Derive(i); auto testSaplingSpendingKey = m.Derive(i);
auto testSaplingPaymentAddress = testSaplingSpendingKey.DefaultAddress(); auto testSaplingPaymentAddress = testSaplingSpendingKey.DefaultAddress();
std::string testSaplingAddr = EncodePaymentAddress(testSaplingPaymentAddress); std::string testSaplingAddr = keyIO.EncodePaymentAddress(testSaplingPaymentAddress);
std::string testSaplingKey = EncodeSpendingKey(testSaplingSpendingKey); std::string testSaplingKey = keyIO.EncodeSpendingKey(testSaplingSpendingKey);
BOOST_CHECK_NO_THROW(CallRPC(string("z_importkey ") + testSaplingKey)); BOOST_CHECK_NO_THROW(CallRPC(string("z_importkey ") + testSaplingKey));
BOOST_CHECK_NO_THROW(retValue = CallRPC(string("z_exportkey ") + testSaplingAddr)); BOOST_CHECK_NO_THROW(retValue = CallRPC(string("z_exportkey ") + testSaplingAddr));
BOOST_CHECK_EQUAL(retValue.get_str(), testSaplingKey); BOOST_CHECK_EQUAL(retValue.get_str(), testSaplingKey);
@ -659,7 +696,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport)
// Make new addresses for the set // Make new addresses for the set
for (int i=0; i<n2; i++) { for (int i=0; i<n2; i++) {
myaddrs.insert(EncodePaymentAddress(pwalletMain->GenerateNewSproutZKey())); myaddrs.insert(keyIO.EncodePaymentAddress(pwalletMain->GenerateNewSproutZKey()));
} }
// Verify number of addresses stored in wallet is n1+n2 // Verify number of addresses stored in wallet is n1+n2
@ -706,17 +743,18 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_getnewaddress) {
pwalletMain->GenerateNewSeed(); pwalletMain->GenerateNewSeed();
} }
KeyIO keyIO(Params());
// No parameter defaults to sapling address // No parameter defaults to sapling address
addr = CallRPC("z_getnewaddress"); addr = CallRPC("z_getnewaddress");
CheckHaveAddr<SaplingPaymentAddress>(DecodePaymentAddress(addr.get_str())); CheckHaveAddr<SaplingPaymentAddress>(keyIO.DecodePaymentAddress(addr.get_str()));
// Passing 'sapling' should also work // Passing 'sapling' should also work
addr = CallRPC("z_getnewaddress sapling"); addr = CallRPC("z_getnewaddress sapling");
CheckHaveAddr<SaplingPaymentAddress>(DecodePaymentAddress(addr.get_str())); CheckHaveAddr<SaplingPaymentAddress>(keyIO.DecodePaymentAddress(addr.get_str()));
// Should also support sprout // Should also support sprout
addr = CallRPC("z_getnewaddress sprout"); addr = CallRPC("z_getnewaddress sprout");
CheckHaveAddr<SproutPaymentAddress>(DecodePaymentAddress(addr.get_str())); CheckHaveAddr<SproutPaymentAddress>(keyIO.DecodePaymentAddress(addr.get_str()));
// Should throw on invalid argument // Should throw on invalid argument
CheckRPCThrows("z_getnewaddress garbage", "Invalid address type"); CheckRPCThrows("z_getnewaddress garbage", "Invalid address type");
@ -1039,7 +1077,8 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters)
std::fill(v.begin(),v.end(), 'A'); std::fill(v.begin(),v.end(), 'A');
std::string badmemo(v.begin(), v.end()); std::string badmemo(v.begin(), v.end());
auto pa = pwalletMain->GenerateNewSproutZKey(); auto pa = pwalletMain->GenerateNewSproutZKey();
std::string zaddr1 = EncodePaymentAddress(pa); KeyIO keyIO(Params());
std::string zaddr1 = keyIO.EncodePaymentAddress(pa);
BOOST_CHECK_THROW(CallRPC(string("z_sendmany tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ ") BOOST_CHECK_THROW(CallRPC(string("z_sendmany tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ ")
+ "[{\"address\":\"" + zaddr1 + "\", \"amount\":123.456}]"), runtime_error); + "[{\"address\":\"" + zaddr1 + "\", \"amount\":123.456}]"), runtime_error);
@ -1130,7 +1169,8 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress")); BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress"));
std::string taddr1 = retValue.get_str(); std::string taddr1 = retValue.get_str();
auto pa = pwalletMain->GenerateNewSproutZKey(); auto pa = pwalletMain->GenerateNewSproutZKey();
std::string zaddr1 = EncodePaymentAddress(pa); KeyIO keyIO(Params());
std::string zaddr1 = keyIO.EncodePaymentAddress(pa);
// there are no utxos to spend // there are no utxos to spend
{ {
@ -1328,11 +1368,12 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling)
UniValue retValue; UniValue retValue;
KeyIO keyIO(Params());
// add keys manually // add keys manually
auto taddr = pwalletMain->GenerateNewKey().GetID(); auto taddr = pwalletMain->GenerateNewKey().GetID();
std::string taddr1 = EncodeDestination(taddr); std::string taddr1 = keyIO.EncodeDestination(taddr);
auto pa = pwalletMain->GenerateNewSaplingZKey(); auto pa = pwalletMain->GenerateNewSaplingZKey();
std::string zaddr1 = EncodePaymentAddress(pa); std::string zaddr1 = keyIO.EncodePaymentAddress(pa);
auto consensusParams = Params().GetConsensus(); auto consensusParams = Params().GetConsensus();
retValue = CallRPC("getblockcount"); retValue = CallRPC("getblockcount");
@ -1677,8 +1718,9 @@ BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_internals)
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight + 1); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight + 1);
// Add keys manually // Add keys manually
KeyIO keyIO(Params());
auto pa = pwalletMain->GenerateNewSproutZKey(); auto pa = pwalletMain->GenerateNewSproutZKey();
std::string zaddr = EncodePaymentAddress(pa); std::string zaddr = keyIO.EncodePaymentAddress(pa);
// Insufficient funds // Insufficient funds
{ {
@ -1885,7 +1927,8 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_internals)
BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress")); BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress"));
MergeToAddressRecipient taddr1(retValue.get_str(), ""); MergeToAddressRecipient taddr1(retValue.get_str(), "");
auto pa = pwalletMain->GenerateNewSproutZKey(); auto pa = pwalletMain->GenerateNewSproutZKey();
MergeToAddressRecipient zaddr1(EncodePaymentAddress(pa), "DEADBEEF"); KeyIO keyIO(Params());
MergeToAddressRecipient zaddr1(keyIO.EncodePaymentAddress(pa), "DEADBEEF");
// Insufficient funds // Insufficient funds
{ {

View File

@ -465,9 +465,10 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
/* A sanity check was added in pull #3843 to avoid adding redeemScripts /* A sanity check was added in pull #3843 to avoid adding redeemScripts
* that never can be redeemed. However, old wallets may still contain * that never can be redeemed. However, old wallets may still contain
* these. Do not add them to the wallet and warn. */ * these. Do not add them to the wallet and warn. */
KeyIO keyIO(Params());
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
{ {
std::string strAddr = EncodeDestination(CScriptID(redeemScript)); std::string strAddr = keyIO.EncodeDestination(CScriptID(redeemScript));
LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n",
__func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr); __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr);
return true; return true;
@ -2284,13 +2285,14 @@ std::pair<SproutNotePlaintext, SproutPaymentAddress> CWalletTx::DecryptSproutNot
auto nd = this->mapSproutNoteData.at(jsop); auto nd = this->mapSproutNoteData.at(jsop);
SproutPaymentAddress pa = nd.address; SproutPaymentAddress pa = nd.address;
KeyIO keyIO(Params());
// Get cached decryptor // Get cached decryptor
ZCNoteDecryption decryptor; ZCNoteDecryption decryptor;
if (!pwallet->GetNoteDecryptor(pa, decryptor)) { if (!pwallet->GetNoteDecryptor(pa, decryptor)) {
// Note decryptors are created when the wallet is loaded, so it should always exist // Note decryptors are created when the wallet is loaded, so it should always exist
throw std::runtime_error(strprintf( throw std::runtime_error(strprintf(
"Could not find note decryptor for payment address %s", "Could not find note decryptor for payment address %s",
EncodePaymentAddress(pa))); keyIO.EncodePaymentAddress(pa)));
} }
auto hSig = this->vJoinSplit[jsop.js].h_sig(this->joinSplitPubKey); auto hSig = this->vJoinSplit[jsop.js].h_sig(this->joinSplitPubKey);
@ -2307,12 +2309,12 @@ std::pair<SproutNotePlaintext, SproutPaymentAddress> CWalletTx::DecryptSproutNot
// Couldn't decrypt with this spending key // Couldn't decrypt with this spending key
throw std::runtime_error(strprintf( throw std::runtime_error(strprintf(
"Could not decrypt note for payment address %s", "Could not decrypt note for payment address %s",
EncodePaymentAddress(pa))); keyIO.EncodePaymentAddress(pa)));
} catch (const std::exception &exc) { } catch (const std::exception &exc) {
// Unexpected failure // Unexpected failure
throw std::runtime_error(strprintf( throw std::runtime_error(strprintf(
"Error while decrypting note for payment address %s: %s", "Error while decrypting note for payment address %s: %s",
EncodePaymentAddress(pa), exc.what())); keyIO.EncodePaymentAddress(pa), exc.what()));
} }
} }
@ -3968,22 +3970,24 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const string& strNam
} }
NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO, NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
KeyIO keyIO(Params());
if (!fFileBacked) if (!fFileBacked)
return false; return false;
if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(EncodeDestination(address), strPurpose)) if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(keyIO.EncodeDestination(address), strPurpose))
return false; return false;
return CWalletDB(strWalletFile).WriteName(EncodeDestination(address), strName); return CWalletDB(strWalletFile).WriteName(keyIO.EncodeDestination(address), strName);
} }
bool CWallet::DelAddressBook(const CTxDestination& address) bool CWallet::DelAddressBook(const CTxDestination& address)
{ {
KeyIO keyIO(Params());
{ {
LOCK(cs_wallet); // mapAddressBook LOCK(cs_wallet); // mapAddressBook
if(fFileBacked) if(fFileBacked)
{ {
// Delete destdata tuples associated with address // Delete destdata tuples associated with address
std::string strAddress = EncodeDestination(address); std::string strAddress = keyIO.EncodeDestination(address);
BOOST_FOREACH(const PAIRTYPE(string, string) &item, mapAddressBook[address].destdata) BOOST_FOREACH(const PAIRTYPE(string, string) &item, mapAddressBook[address].destdata)
{ {
CWalletDB(strWalletFile).EraseDestData(strAddress, item.first); CWalletDB(strWalletFile).EraseDestData(strAddress, item.first);
@ -3996,8 +4000,8 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
if (!fFileBacked) if (!fFileBacked)
return false; return false;
CWalletDB(strWalletFile).ErasePurpose(EncodeDestination(address)); CWalletDB(strWalletFile).ErasePurpose(keyIO.EncodeDestination(address));
return CWalletDB(strWalletFile).EraseName(EncodeDestination(address)); return CWalletDB(strWalletFile).EraseName(keyIO.EncodeDestination(address));
} }
bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) bool CWallet::SetDefaultKey(const CPubKey &vchPubKey)
@ -4571,7 +4575,8 @@ bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, co
mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
if (!fFileBacked) if (!fFileBacked)
return true; return true;
return CWalletDB(strWalletFile).WriteDestData(EncodeDestination(dest), key, value); KeyIO keyIO(Params());
return CWalletDB(strWalletFile).WriteDestData(keyIO.EncodeDestination(dest), key, value);
} }
bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key)
@ -4580,7 +4585,8 @@ bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key)
return false; return false;
if (!fFileBacked) if (!fFileBacked)
return true; return true;
return CWalletDB(strWalletFile).EraseDestData(EncodeDestination(dest), key); KeyIO keyIO(Params());
return CWalletDB(strWalletFile).EraseDestData(keyIO.EncodeDestination(dest), key);
} }
bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value) bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
@ -4849,10 +4855,11 @@ bool CWallet::ParameterInteraction()
bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS); fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS);
KeyIO keyIO(Params());
// Check Sapling migration address if set and is a valid Sapling address // Check Sapling migration address if set and is a valid Sapling address
if (mapArgs.count("-migrationdestaddress")) { if (mapArgs.count("-migrationdestaddress")) {
std::string migrationDestAddress = mapArgs["-migrationdestaddress"]; std::string migrationDestAddress = mapArgs["-migrationdestaddress"];
libzcash::PaymentAddress address = DecodePaymentAddress(migrationDestAddress); libzcash::PaymentAddress address = keyIO.DecodePaymentAddress(migrationDestAddress);
if (boost::get<libzcash::SaplingPaymentAddress>(&address) == nullptr) { if (boost::get<libzcash::SaplingPaymentAddress>(&address) == nullptr) {
return UIError(_("-migrationdestaddress must be a valid Sapling address.")); return UIError(_("-migrationdestaddress must be a valid Sapling address."));
} }
@ -4964,8 +4971,9 @@ void CWallet::GetFilteredNotes(
{ {
std::set<PaymentAddress> filterAddresses; std::set<PaymentAddress> filterAddresses;
KeyIO keyIO(Params());
if (address.length() > 0) { if (address.length() > 0) {
filterAddresses.insert(DecodePaymentAddress(address)); filterAddresses.insert(keyIO.DecodePaymentAddress(address));
} }
GetFilteredNotes(sproutEntries, saplingEntries, filterAddresses, minDepth, INT_MAX, ignoreSpent, requireSpendingKey); GetFilteredNotes(sproutEntries, saplingEntries, filterAddresses, minDepth, INT_MAX, ignoreSpent, requireSpendingKey);
@ -4988,6 +4996,7 @@ void CWallet::GetFilteredNotes(
{ {
LOCK2(cs_main, cs_wallet); LOCK2(cs_main, cs_wallet);
KeyIO keyIO(Params());
for (auto & p : mapWallet) { for (auto & p : mapWallet) {
CWalletTx wtx = p.second; CWalletTx wtx = p.second;
@ -5035,7 +5044,7 @@ void CWallet::GetFilteredNotes(
ZCNoteDecryption decryptor; ZCNoteDecryption decryptor;
if (!GetNoteDecryptor(pa, decryptor)) { if (!GetNoteDecryptor(pa, decryptor)) {
// Note decryptors are created when the wallet is loaded, so it should always exist // Note decryptors are created when the wallet is loaded, so it should always exist
throw std::runtime_error(strprintf("Could not find note decryptor for payment address %s", EncodePaymentAddress(pa))); throw std::runtime_error(strprintf("Could not find note decryptor for payment address %s", keyIO.EncodePaymentAddress(pa)));
} }
// determine amount of funds in the note // determine amount of funds in the note
@ -5053,10 +5062,10 @@ void CWallet::GetFilteredNotes(
} catch (const note_decryption_failed &err) { } catch (const note_decryption_failed &err) {
// Couldn't decrypt with this spending key // Couldn't decrypt with this spending key
throw std::runtime_error(strprintf("Could not decrypt note for payment address %s", EncodePaymentAddress(pa))); throw std::runtime_error(strprintf("Could not decrypt note for payment address %s", keyIO.EncodePaymentAddress(pa)));
} catch (const std::exception &exc) { } catch (const std::exception &exc) {
// Unexpected failure // Unexpected failure
throw std::runtime_error(strprintf("Error while decrypting note for payment address %s: %s", EncodePaymentAddress(pa), exc.what())); throw std::runtime_error(strprintf("Error while decrypting note for payment address %s: %s", keyIO.EncodePaymentAddress(pa), exc.what()));
} }
} }
@ -5243,8 +5252,9 @@ KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::InvalidEncoding&
KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKey &sk) const { KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKey &sk) const {
auto addr = sk.address(); auto addr = sk.address();
KeyIO keyIO(Params());
if (log){ if (log){
LogPrint("zrpc", "Importing zaddr %s...\n", EncodePaymentAddress(addr)); LogPrint("zrpc", "Importing zaddr %s...\n", keyIO.EncodePaymentAddress(addr));
} }
if (m_wallet->HaveSproutSpendingKey(addr)) { if (m_wallet->HaveSproutSpendingKey(addr)) {
return KeyAlreadyExists; return KeyAlreadyExists;
@ -5259,9 +5269,10 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKe
KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedSpendingKey &sk) const { KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedSpendingKey &sk) const {
auto extfvk = sk.ToXFVK(); auto extfvk = sk.ToXFVK();
auto ivk = extfvk.fvk.in_viewing_key(); auto ivk = extfvk.fvk.in_viewing_key();
KeyIO keyIO(Params());
{ {
if (log){ if (log){
LogPrint("zrpc", "Importing zaddr %s...\n", EncodePaymentAddress(sk.DefaultAddress())); LogPrint("zrpc", "Importing zaddr %s...\n", keyIO.EncodePaymentAddress(sk.DefaultAddress()));
} }
// Don't throw error in case a key is already there // Don't throw error in case a key is already there
if (m_wallet->HaveSaplingSpendingKey(extfvk)) { if (m_wallet->HaveSaplingSpendingKey(extfvk)) {

View File

@ -458,6 +458,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
CWalletScanState &wss, string& strType, string& strErr) CWalletScanState &wss, string& strType, string& strErr)
{ {
try { try {
KeyIO keyIO(Params());
// Unserialize // Unserialize
// Taking advantage of the fact that pair serialization // Taking advantage of the fact that pair serialization
// is just the two items serialized one after the other // is just the two items serialized one after the other
@ -466,13 +468,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
{ {
string strAddress; string strAddress;
ssKey >> strAddress; ssKey >> strAddress;
ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].name; ssValue >> pwallet->mapAddressBook[keyIO.DecodeDestination(strAddress)].name;
} }
else if (strType == "purpose") else if (strType == "purpose")
{ {
string strAddress; string strAddress;
ssKey >> strAddress; ssKey >> strAddress;
ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].purpose; ssValue >> pwallet->mapAddressBook[keyIO.DecodeDestination(strAddress)].purpose;
} }
else if (strType == "tx") else if (strType == "tx")
{ {
@ -829,7 +831,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssKey >> strAddress; ssKey >> strAddress;
ssKey >> strKey; ssKey >> strKey;
ssValue >> strValue; ssValue >> strValue;
if (!pwallet->LoadDestData(DecodeDestination(strAddress), strKey, strValue)) if (!pwallet->LoadDestData(keyIO.DecodeDestination(strAddress), strKey, strValue))
{ {
strErr = "Error reading wallet database: LoadDestData failed"; strErr = "Error reading wallet database: LoadDestData failed";
return false; return false;

View File

@ -68,6 +68,7 @@ enum class Zip212Enabled {
BeforeZip212, BeforeZip212,
AfterZip212 AfterZip212
}; };
class SaplingNote : public BaseNote { class SaplingNote : public BaseNote {
private: private:
uint256 rseed; uint256 rseed;