Auto merge of #4256 - str4d:zip-213-shielded-coinbase, r=daira
[NU3 Heartwood] Shielded Coinbase Implements [ZIP 213](https://github.com/zcash/zips/pull/217).
This commit is contained in:
commit
35bff6ac7c
|
@ -81,6 +81,7 @@ testScripts=(
|
||||||
'shorter_block_times.py'
|
'shorter_block_times.py'
|
||||||
'sprout_sapling_migration.py'
|
'sprout_sapling_migration.py'
|
||||||
'turnstile.py'
|
'turnstile.py'
|
||||||
|
'mining_shielded_coinbase.py'
|
||||||
);
|
);
|
||||||
testScriptsExt=(
|
testScriptsExt=(
|
||||||
'getblocktemplate_longpoll.py'
|
'getblocktemplate_longpoll.py'
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright (c) 2019 The Zcash developers
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file COPYING or https://www.opensource.org/licenses/mit-license.php .
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
from test_framework.authproxy import JSONRPCException
|
||||||
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
|
from test_framework.util import (
|
||||||
|
assert_equal,
|
||||||
|
assert_raises,
|
||||||
|
bitcoind_processes,
|
||||||
|
connect_nodes,
|
||||||
|
initialize_chain_clean,
|
||||||
|
start_node,
|
||||||
|
wait_and_assert_operationid_status,
|
||||||
|
)
|
||||||
|
|
||||||
|
class ShieldCoinbaseTest (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=2bb40e60:1", # Blossom
|
||||||
|
"-nuparams=f5b9230b:10", # Heartwood
|
||||||
|
"-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 Sapling address for node 1
|
||||||
|
node1_zaddr = self.nodes[1].z_getnewaddress('sapling')
|
||||||
|
|
||||||
|
self.nodes[1].stop()
|
||||||
|
bitcoind_processes[1].wait()
|
||||||
|
self.nodes[1] = self.start_node_with(1, [
|
||||||
|
"-mineraddress=%s" % node1_zaddr,
|
||||||
|
])
|
||||||
|
connect_nodes(self.nodes[1], 0)
|
||||||
|
|
||||||
|
# Node 0 can mine blocks, because it is targeting a transparent address
|
||||||
|
print("Mining block with node 0")
|
||||||
|
self.nodes[0].generate(1)
|
||||||
|
self.sync_all()
|
||||||
|
walletinfo = self.nodes[0].getwalletinfo()
|
||||||
|
assert_equal(walletinfo['immature_balance'], 5)
|
||||||
|
assert_equal(walletinfo['balance'], 0)
|
||||||
|
|
||||||
|
# Node 1 cannot mine blocks, because it is targeting a Sapling address
|
||||||
|
# but Heartwood is not yet active
|
||||||
|
print("Attempting to mine block with node 1")
|
||||||
|
assert_raises(JSONRPCException, self.nodes[1].generate, 1)
|
||||||
|
assert_equal(self.nodes[1].z_getbalance(node1_zaddr, 0), 0)
|
||||||
|
assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 0)
|
||||||
|
|
||||||
|
# Stop node 1 and check logs to verify the block was rejected correctly
|
||||||
|
print("Checking node 1 logs")
|
||||||
|
self.nodes[1].stop()
|
||||||
|
bitcoind_processes[1].wait()
|
||||||
|
logpath = self.options.tmpdir + "/node1/regtest/debug.log"
|
||||||
|
foundErrorMsg = False
|
||||||
|
with open(logpath, "r") as myfile:
|
||||||
|
logdata = myfile.readlines()
|
||||||
|
for logline in logdata:
|
||||||
|
if "CheckTransaction(): coinbase has output descriptions" in logline:
|
||||||
|
foundErrorMsg = True
|
||||||
|
break
|
||||||
|
assert(foundErrorMsg)
|
||||||
|
|
||||||
|
# Restart node 1
|
||||||
|
self.nodes[1] = self.start_node_with(1, [
|
||||||
|
"-mineraddress=%s" % node1_zaddr,
|
||||||
|
])
|
||||||
|
connect_nodes(self.nodes[1], 0)
|
||||||
|
|
||||||
|
# Activate Heartwood
|
||||||
|
print("Activating Heartwood")
|
||||||
|
self.nodes[0].generate(8)
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
|
# Node 1 can now mine blocks!
|
||||||
|
print("Mining block with node 1")
|
||||||
|
self.nodes[1].generate(1)
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
|
# Transparent coinbase outputs are subject to coinbase maturity
|
||||||
|
assert_equal(self.nodes[0].getbalance(), Decimal('0'))
|
||||||
|
assert_equal(self.nodes[0].z_gettotalbalance()['transparent'], '0.00')
|
||||||
|
assert_equal(self.nodes[0].z_gettotalbalance()['private'], '0.00')
|
||||||
|
assert_equal(self.nodes[0].z_gettotalbalance()['total'], '0.00')
|
||||||
|
|
||||||
|
# Shielded coinbase outputs are not subject to coinbase maturity
|
||||||
|
assert_equal(self.nodes[1].z_getbalance(node1_zaddr, 0), 5)
|
||||||
|
assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 5)
|
||||||
|
assert_equal(self.nodes[1].z_gettotalbalance()['private'], '5.00')
|
||||||
|
assert_equal(self.nodes[1].z_gettotalbalance()['total'], '5.00')
|
||||||
|
|
||||||
|
# Send from Sapling coinbase to Sapling address and transparent address
|
||||||
|
# (to check that a non-empty vout is allowed when spending shielded
|
||||||
|
# coinbase)
|
||||||
|
print("Sending Sapling coinbase to Sapling address")
|
||||||
|
node0_zaddr = self.nodes[0].z_getnewaddress('sapling')
|
||||||
|
node0_taddr = self.nodes[0].getnewaddress()
|
||||||
|
recipients = []
|
||||||
|
recipients.append({"address": node0_zaddr, "amount": Decimal('2')})
|
||||||
|
recipients.append({"address": node0_taddr, "amount": Decimal('2')})
|
||||||
|
myopid = self.nodes[1].z_sendmany(node1_zaddr, recipients, 1, 0)
|
||||||
|
wait_and_assert_operationid_status(self.nodes[1], myopid)
|
||||||
|
self.sync_all()
|
||||||
|
self.nodes[0].generate(1)
|
||||||
|
self.sync_all()
|
||||||
|
|
||||||
|
assert_equal(self.nodes[0].z_getbalance(node0_zaddr), 2)
|
||||||
|
assert_equal(self.nodes[0].z_getbalance(node0_taddr), 2)
|
||||||
|
assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 1)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
ShieldCoinbaseTest().main()
|
|
@ -5,8 +5,11 @@
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
#include "consensus/validation.h"
|
#include "consensus/validation.h"
|
||||||
|
#include "transaction_builder.h"
|
||||||
#include "utiltest.h"
|
#include "utiltest.h"
|
||||||
|
|
||||||
|
#include <librustzcash.h>
|
||||||
|
|
||||||
extern ZCJoinSplit* params;
|
extern ZCJoinSplit* params;
|
||||||
|
|
||||||
TEST(ChecktransactionTests, CheckVpubNotBothNonzero) {
|
TEST(ChecktransactionTests, CheckVpubNotBothNonzero) {
|
||||||
|
@ -1027,3 +1030,191 @@ TEST(ChecktransactionTests, BadTxReceivedOverNetwork)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(CheckTransaction, InvalidShieldedCoinbase) {
|
||||||
|
RegtestActivateSapling();
|
||||||
|
|
||||||
|
CMutableTransaction mtx = GetValidTransaction();
|
||||||
|
mtx.fOverwintered = true;
|
||||||
|
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
|
||||||
|
mtx.nVersion = SAPLING_TX_VERSION;
|
||||||
|
|
||||||
|
// Make it an invalid shielded coinbase (no ciphertexts or commitments).
|
||||||
|
mtx.vin.resize(1);
|
||||||
|
mtx.vin[0].prevout.SetNull();
|
||||||
|
mtx.vShieldedOutput.resize(1);
|
||||||
|
mtx.vJoinSplit.resize(0);
|
||||||
|
|
||||||
|
CTransaction tx(mtx);
|
||||||
|
EXPECT_TRUE(tx.IsCoinBase());
|
||||||
|
|
||||||
|
// Before Heartwood, output descriptions are rejected.
|
||||||
|
MockCValidationState state;
|
||||||
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-has-output-description", false)).Times(1);
|
||||||
|
ContextualCheckTransaction(tx, state, Params(), 10, 57);
|
||||||
|
|
||||||
|
RegtestActivateHeartwood(false, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||||
|
|
||||||
|
// From Heartwood, the output description is allowed but invalid (undecryptable).
|
||||||
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-output-desc-invalid-outct", false)).Times(1);
|
||||||
|
ContextualCheckTransaction(tx, state, Params(), 10, 57);
|
||||||
|
|
||||||
|
RegtestDeactivateHeartwood();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CheckTransaction, HeartwoodAcceptsShieldedCoinbase) {
|
||||||
|
RegtestActivateHeartwood(false, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||||
|
auto chainparams = Params();
|
||||||
|
|
||||||
|
uint256 ovk;
|
||||||
|
auto note = libzcash::SaplingNote(
|
||||||
|
libzcash::SaplingSpendingKey::random().default_address(), CAmount(123456));
|
||||||
|
auto output = OutputDescriptionInfo(ovk, note, {{0xF6}});
|
||||||
|
|
||||||
|
auto ctx = librustzcash_sapling_proving_ctx_init();
|
||||||
|
auto odesc = output.Build(ctx).get();
|
||||||
|
librustzcash_sapling_proving_ctx_free(ctx);
|
||||||
|
|
||||||
|
CMutableTransaction mtx = GetValidTransaction();
|
||||||
|
mtx.fOverwintered = true;
|
||||||
|
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
|
||||||
|
mtx.nVersion = SAPLING_TX_VERSION;
|
||||||
|
|
||||||
|
mtx.vin.resize(1);
|
||||||
|
mtx.vin[0].prevout.SetNull();
|
||||||
|
mtx.vJoinSplit.resize(0);
|
||||||
|
mtx.vShieldedOutput.push_back(odesc);
|
||||||
|
|
||||||
|
// Transaction should fail with a bad public cmu.
|
||||||
|
{
|
||||||
|
auto cmOrig = mtx.vShieldedOutput[0].cmu;
|
||||||
|
mtx.vShieldedOutput[0].cmu = uint256S("1234");
|
||||||
|
CTransaction tx(mtx);
|
||||||
|
EXPECT_TRUE(tx.IsCoinBase());
|
||||||
|
|
||||||
|
MockCValidationState state;
|
||||||
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-output-desc-invalid-outct", false)).Times(1);
|
||||||
|
ContextualCheckTransaction(tx, state, chainparams, 10, 57);
|
||||||
|
|
||||||
|
mtx.vShieldedOutput[0].cmu = cmOrig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transaction should fail with a bad outCiphertext.
|
||||||
|
{
|
||||||
|
auto outCtOrig = mtx.vShieldedOutput[0].outCiphertext;
|
||||||
|
mtx.vShieldedOutput[0].outCiphertext = {{}};
|
||||||
|
CTransaction tx(mtx);
|
||||||
|
EXPECT_TRUE(tx.IsCoinBase());
|
||||||
|
|
||||||
|
MockCValidationState state;
|
||||||
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-output-desc-invalid-outct", false)).Times(1);
|
||||||
|
ContextualCheckTransaction(tx, state, chainparams, 10, 57);
|
||||||
|
|
||||||
|
mtx.vShieldedOutput[0].outCiphertext = outCtOrig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transaction should fail with a bad encCiphertext.
|
||||||
|
{
|
||||||
|
auto encCtOrig = mtx.vShieldedOutput[0].encCiphertext;
|
||||||
|
mtx.vShieldedOutput[0].encCiphertext = {{}};
|
||||||
|
CTransaction tx(mtx);
|
||||||
|
EXPECT_TRUE(tx.IsCoinBase());
|
||||||
|
|
||||||
|
MockCValidationState state;
|
||||||
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-output-desc-invalid-encct", false)).Times(1);
|
||||||
|
ContextualCheckTransaction(tx, state, chainparams, 10, 57);
|
||||||
|
|
||||||
|
mtx.vShieldedOutput[0].encCiphertext = encCtOrig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the success case. The unmodified transaction should fail signature
|
||||||
|
// validation, which is an unrelated consensus rule and is checked after the
|
||||||
|
// shielded coinbase rules have passed.
|
||||||
|
{
|
||||||
|
CTransaction tx(mtx);
|
||||||
|
EXPECT_TRUE(tx.IsCoinBase());
|
||||||
|
|
||||||
|
MockCValidationState state;
|
||||||
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-sapling-binding-signature-invalid", false)).Times(1);
|
||||||
|
ContextualCheckTransaction(tx, state, chainparams, 10, 57);
|
||||||
|
}
|
||||||
|
|
||||||
|
RegtestDeactivateHeartwood();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the consensus rules relevant to valueBalance, vShieldedOutput, and
|
||||||
|
// bindingSig from https://zips.z.cash/protocol/protocol.pdf#txnencoding are
|
||||||
|
// applied to coinbase transactions.
|
||||||
|
TEST(CheckTransaction, HeartwoodEnforcesSaplingRulesOnShieldedCoinbase) {
|
||||||
|
RegtestActivateHeartwood(false, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||||
|
auto chainparams = Params();
|
||||||
|
|
||||||
|
uint256 ovk;
|
||||||
|
auto note = libzcash::SaplingNote(
|
||||||
|
libzcash::SaplingSpendingKey::random().default_address(), CAmount(123456));
|
||||||
|
auto output = OutputDescriptionInfo(ovk, note, {{0xF6}});
|
||||||
|
|
||||||
|
CMutableTransaction mtx = GetValidTransaction();
|
||||||
|
mtx.fOverwintered = true;
|
||||||
|
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
|
||||||
|
mtx.nVersion = SAPLING_TX_VERSION;
|
||||||
|
|
||||||
|
mtx.vin.resize(1);
|
||||||
|
mtx.vin[0].prevout.SetNull();
|
||||||
|
mtx.vin[0].scriptSig << 123;
|
||||||
|
mtx.vJoinSplit.resize(0);
|
||||||
|
mtx.valueBalance = -1000;
|
||||||
|
|
||||||
|
// Coinbase transaction should fail non-contextual checks with no shielded
|
||||||
|
// outputs and non-zero valueBalance.
|
||||||
|
{
|
||||||
|
CTransaction tx(mtx);
|
||||||
|
EXPECT_TRUE(tx.IsCoinBase());
|
||||||
|
|
||||||
|
MockCValidationState state;
|
||||||
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-nonzero", false)).Times(1);
|
||||||
|
EXPECT_FALSE(CheckTransactionWithoutProofVerification(tx, state));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a Sapling output.
|
||||||
|
auto ctx = librustzcash_sapling_proving_ctx_init();
|
||||||
|
auto odesc = output.Build(ctx).get();
|
||||||
|
librustzcash_sapling_proving_ctx_free(ctx);
|
||||||
|
mtx.vShieldedOutput.push_back(odesc);
|
||||||
|
|
||||||
|
// Coinbase transaction should fail non-contextual checks with valueBalance
|
||||||
|
// out of range.
|
||||||
|
{
|
||||||
|
mtx.valueBalance = MAX_MONEY + 1;
|
||||||
|
CTransaction tx(mtx);
|
||||||
|
EXPECT_TRUE(tx.IsCoinBase());
|
||||||
|
|
||||||
|
MockCValidationState state;
|
||||||
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-toolarge", false)).Times(1);
|
||||||
|
EXPECT_FALSE(CheckTransactionWithoutProofVerification(tx, state));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
mtx.valueBalance = -MAX_MONEY - 1;
|
||||||
|
CTransaction tx(mtx);
|
||||||
|
EXPECT_TRUE(tx.IsCoinBase());
|
||||||
|
|
||||||
|
MockCValidationState state;
|
||||||
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-toolarge", false)).Times(1);
|
||||||
|
EXPECT_FALSE(CheckTransactionWithoutProofVerification(tx, state));
|
||||||
|
}
|
||||||
|
|
||||||
|
mtx.valueBalance = -1000;
|
||||||
|
CTransaction tx(mtx);
|
||||||
|
EXPECT_TRUE(tx.IsCoinBase());
|
||||||
|
|
||||||
|
// Coinbase transaction should now pass non-contextual checks.
|
||||||
|
MockCValidationState state;
|
||||||
|
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
|
||||||
|
|
||||||
|
// Coinbase transaction does not pass contextual checks, as bindingSig
|
||||||
|
// consensus rule is enforced.
|
||||||
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-sapling-binding-signature-invalid", false)).Times(1);
|
||||||
|
ContextualCheckTransaction(tx, state, chainparams, 10, 57);
|
||||||
|
|
||||||
|
RegtestDeactivateHeartwood();
|
||||||
|
}
|
||||||
|
|
|
@ -7,37 +7,37 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
TEST(Miner, GetScriptForMinerAddress) {
|
TEST(Miner, GetMinerAddress) {
|
||||||
SelectParams(CBaseChainParams::MAIN);
|
SelectParams(CBaseChainParams::MAIN);
|
||||||
|
|
||||||
// No miner address set
|
// No miner address set
|
||||||
{
|
{
|
||||||
boost::shared_ptr<CReserveScript> coinbaseScript;
|
MinerAddress minerAddress;
|
||||||
GetScriptForMinerAddress(coinbaseScript);
|
GetMinerAddress(minerAddress);
|
||||||
EXPECT_FALSE((bool) coinbaseScript);
|
EXPECT_FALSE(IsValidMinerAddress(minerAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
mapArgs["-mineraddress"] = "notAnAddress";
|
mapArgs["-mineraddress"] = "notAnAddress";
|
||||||
{
|
{
|
||||||
boost::shared_ptr<CReserveScript> coinbaseScript;
|
MinerAddress minerAddress;
|
||||||
GetScriptForMinerAddress(coinbaseScript);
|
GetMinerAddress(minerAddress);
|
||||||
EXPECT_FALSE((bool) coinbaseScript);
|
EXPECT_FALSE(IsValidMinerAddress(minerAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Partial address
|
// Partial transparent address
|
||||||
mapArgs["-mineraddress"] = "t1T8yaLVhNqxA5KJcmiqq";
|
mapArgs["-mineraddress"] = "t1T8yaLVhNqxA5KJcmiqq";
|
||||||
{
|
{
|
||||||
boost::shared_ptr<CReserveScript> coinbaseScript;
|
MinerAddress minerAddress;
|
||||||
GetScriptForMinerAddress(coinbaseScript);
|
GetMinerAddress(minerAddress);
|
||||||
EXPECT_FALSE((bool) coinbaseScript);
|
EXPECT_FALSE(IsValidMinerAddress(minerAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Typo in address
|
// Typo in transparent address
|
||||||
mapArgs["-mineraddress"] = "t1TByaLVhNqxA5KJcmiqqFN88e8DNp2PBfF";
|
mapArgs["-mineraddress"] = "t1TByaLVhNqxA5KJcmiqqFN88e8DNp2PBfF";
|
||||||
{
|
{
|
||||||
boost::shared_ptr<CReserveScript> coinbaseScript;
|
MinerAddress minerAddress;
|
||||||
GetScriptForMinerAddress(coinbaseScript);
|
GetMinerAddress(minerAddress);
|
||||||
EXPECT_FALSE((bool) coinbaseScript);
|
EXPECT_FALSE(IsValidMinerAddress(minerAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up expected scriptPubKey for t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF
|
// Set up expected scriptPubKey for t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF
|
||||||
|
@ -45,30 +45,77 @@ TEST(Miner, GetScriptForMinerAddress) {
|
||||||
keyID.SetHex("eb88f1c65b39a823479ac9c7db2f4a865960a165");
|
keyID.SetHex("eb88f1c65b39a823479ac9c7db2f4a865960a165");
|
||||||
CScript expectedCoinbaseScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
|
CScript expectedCoinbaseScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||||
|
|
||||||
// Valid address
|
// Valid transparent address
|
||||||
mapArgs["-mineraddress"] = "t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF";
|
mapArgs["-mineraddress"] = "t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF";
|
||||||
{
|
{
|
||||||
boost::shared_ptr<CReserveScript> coinbaseScript;
|
MinerAddress minerAddress;
|
||||||
GetScriptForMinerAddress(coinbaseScript);
|
GetMinerAddress(minerAddress);
|
||||||
EXPECT_TRUE((bool) coinbaseScript);
|
EXPECT_TRUE(IsValidMinerAddress(minerAddress));
|
||||||
|
EXPECT_TRUE(boost::get<boost::shared_ptr<CReserveScript>>(&minerAddress) != nullptr);
|
||||||
|
auto coinbaseScript = boost::get<boost::shared_ptr<CReserveScript>>(minerAddress);
|
||||||
EXPECT_EQ(expectedCoinbaseScript, coinbaseScript->reserveScript);
|
EXPECT_EQ(expectedCoinbaseScript, coinbaseScript->reserveScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid address with leading whitespace
|
// Valid transparent address with leading whitespace
|
||||||
mapArgs["-mineraddress"] = " t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF";
|
mapArgs["-mineraddress"] = " t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF";
|
||||||
{
|
{
|
||||||
boost::shared_ptr<CReserveScript> coinbaseScript;
|
MinerAddress minerAddress;
|
||||||
GetScriptForMinerAddress(coinbaseScript);
|
GetMinerAddress(minerAddress);
|
||||||
EXPECT_TRUE((bool) coinbaseScript);
|
EXPECT_TRUE(IsValidMinerAddress(minerAddress));
|
||||||
|
EXPECT_TRUE(boost::get<boost::shared_ptr<CReserveScript>>(&minerAddress) != nullptr);
|
||||||
|
auto coinbaseScript = boost::get<boost::shared_ptr<CReserveScript>>(minerAddress);
|
||||||
EXPECT_EQ(expectedCoinbaseScript, coinbaseScript->reserveScript);
|
EXPECT_EQ(expectedCoinbaseScript, coinbaseScript->reserveScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid address with trailing whitespace
|
// Valid transparent address with trailing whitespace
|
||||||
mapArgs["-mineraddress"] = "t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF ";
|
mapArgs["-mineraddress"] = "t1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF ";
|
||||||
{
|
{
|
||||||
boost::shared_ptr<CReserveScript> coinbaseScript;
|
MinerAddress minerAddress;
|
||||||
GetScriptForMinerAddress(coinbaseScript);
|
GetMinerAddress(minerAddress);
|
||||||
EXPECT_TRUE((bool) coinbaseScript);
|
EXPECT_TRUE(IsValidMinerAddress(minerAddress));
|
||||||
|
EXPECT_TRUE(boost::get<boost::shared_ptr<CReserveScript>>(&minerAddress) != nullptr);
|
||||||
|
auto coinbaseScript = boost::get<boost::shared_ptr<CReserveScript>>(minerAddress);
|
||||||
EXPECT_EQ(expectedCoinbaseScript, coinbaseScript->reserveScript);
|
EXPECT_EQ(expectedCoinbaseScript, coinbaseScript->reserveScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Partial Sapling address
|
||||||
|
mapArgs["-mineraddress"] = "zs1z7rejlpsa98s2rrrfkwmaxu53";
|
||||||
|
{
|
||||||
|
MinerAddress minerAddress;
|
||||||
|
GetMinerAddress(minerAddress);
|
||||||
|
EXPECT_FALSE(IsValidMinerAddress(minerAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Typo in Sapling address
|
||||||
|
mapArgs["-mineraddress"] = "zs1s7rejlpsa98s2rrrfkwmaxu53e4ue0ulcrw0h4x5g8jl04tak0d3mm47vdtahatqrlkngh9slya";
|
||||||
|
{
|
||||||
|
MinerAddress minerAddress;
|
||||||
|
GetMinerAddress(minerAddress);
|
||||||
|
EXPECT_FALSE(IsValidMinerAddress(minerAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid Sapling address
|
||||||
|
mapArgs["-mineraddress"] = "zs1z7rejlpsa98s2rrrfkwmaxu53e4ue0ulcrw0h4x5g8jl04tak0d3mm47vdtahatqrlkngh9slya";
|
||||||
|
{
|
||||||
|
MinerAddress minerAddress;
|
||||||
|
GetMinerAddress(minerAddress);
|
||||||
|
EXPECT_TRUE(IsValidMinerAddress(minerAddress));
|
||||||
|
EXPECT_TRUE(boost::get<libzcash::SaplingPaymentAddress>(&minerAddress) != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid Sapling address with leading whitespace
|
||||||
|
mapArgs["-mineraddress"] = " zs1z7rejlpsa98s2rrrfkwmaxu53e4ue0ulcrw0h4x5g8jl04tak0d3mm47vdtahatqrlkngh9slya";
|
||||||
|
{
|
||||||
|
MinerAddress minerAddress;
|
||||||
|
GetMinerAddress(minerAddress);
|
||||||
|
EXPECT_FALSE(IsValidMinerAddress(minerAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid Sapling address with trailing whitespace
|
||||||
|
mapArgs["-mineraddress"] = "zs1z7rejlpsa98s2rrrfkwmaxu53e4ue0ulcrw0h4x5g8jl04tak0d3mm47vdtahatqrlkngh9slya ";
|
||||||
|
{
|
||||||
|
MinerAddress minerAddress;
|
||||||
|
GetMinerAddress(minerAddress);
|
||||||
|
EXPECT_FALSE(IsValidMinerAddress(minerAddress));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ TEST(TransactionBuilder, TransparentToSapling)
|
||||||
// Create a shielding transaction from transparent to Sapling
|
// Create a shielding transaction from transparent to Sapling
|
||||||
// 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee
|
// 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee
|
||||||
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
|
builder.AddTransparentInput(COutPoint(uint256S("1234"), 0), scriptPubKey, 50000);
|
||||||
builder.AddSaplingOutput(fvk_from.ovk, pk, 40000, {});
|
builder.AddSaplingOutput(fvk_from.ovk, pk, 40000, {});
|
||||||
auto tx = builder.Build().GetTxOrThrow();
|
auto tx = builder.Build().GetTxOrThrow();
|
||||||
|
|
||||||
|
|
41
src/init.cpp
41
src/init.cpp
|
@ -437,6 +437,7 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||||
strUsage += HelpMessageOpt("-fuzzmessagestest=<n>", "Randomly fuzz 1 of every <n> network messages");
|
strUsage += HelpMessageOpt("-fuzzmessagestest=<n>", "Randomly fuzz 1 of every <n> network messages");
|
||||||
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));
|
||||||
}
|
}
|
||||||
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
|
||||||
|
@ -1037,11 +1038,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||||
if (mapArgs.count("-mineraddress")) {
|
if (mapArgs.count("-mineraddress")) {
|
||||||
CTxDestination addr = DecodeDestination(mapArgs["-mineraddress"]);
|
CTxDestination addr = DecodeDestination(mapArgs["-mineraddress"]);
|
||||||
if (!IsValidDestination(addr)) {
|
if (!IsValidDestination(addr)) {
|
||||||
|
// Try a Sapling address
|
||||||
|
auto zaddr = DecodePaymentAddress(mapArgs["-mineraddress"]);
|
||||||
|
if (!IsValidPaymentAddress(zaddr) ||
|
||||||
|
boost::get<libzcash::SaplingPaymentAddress>(&zaddr) == nullptr)
|
||||||
|
{
|
||||||
return InitError(strprintf(
|
return InitError(strprintf(
|
||||||
_("Invalid address for -mineraddress=<addr>: '%s' (must be a transparent address)"),
|
_("Invalid address for -mineraddress=<addr>: '%s' (must be a Sapling or transparent address)"),
|
||||||
mapArgs["-mineraddress"]));
|
mapArgs["-mineraddress"]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!mapMultiArgs["-nuparams"].empty()) {
|
if (!mapMultiArgs["-nuparams"].empty()) {
|
||||||
|
@ -1077,6 +1084,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mapArgs.count("-nurejectoldversions")) {
|
||||||
|
if (Params().NetworkIDString() != "regtest") {
|
||||||
|
return InitError("-nurejectoldversions may only be set on regtest.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log
|
// ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log
|
||||||
|
|
||||||
// Initialize libsodium
|
// Initialize libsodium
|
||||||
|
@ -1517,10 +1530,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
bool minerAddressInLocalWallet = false;
|
bool minerAddressInLocalWallet = false;
|
||||||
if (pwalletMain) {
|
if (pwalletMain) {
|
||||||
// Address has already been validated
|
|
||||||
CTxDestination addr = DecodeDestination(mapArgs["-mineraddress"]);
|
CTxDestination addr = DecodeDestination(mapArgs["-mineraddress"]);
|
||||||
|
if (IsValidDestination(addr)) {
|
||||||
CKeyID keyID = boost::get<CKeyID>(addr);
|
CKeyID keyID = boost::get<CKeyID>(addr);
|
||||||
minerAddressInLocalWallet = pwalletMain->HaveKey(keyID);
|
minerAddressInLocalWallet = pwalletMain->HaveKey(keyID);
|
||||||
|
} else {
|
||||||
|
auto zaddr = DecodePaymentAddress(mapArgs["-mineraddress"]);
|
||||||
|
minerAddressInLocalWallet = boost::apply_visitor(
|
||||||
|
HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (GetBoolArg("-minetolocalwallet", true) && !minerAddressInLocalWallet) {
|
if (GetBoolArg("-minetolocalwallet", true) && !minerAddressInLocalWallet) {
|
||||||
return InitError(_("-mineraddress is not in the local wallet. Either use a local address, or set -minetolocalwallet=0"));
|
return InitError(_("-mineraddress is not in the local wallet. Either use a local address, or set -minetolocalwallet=0"));
|
||||||
|
@ -1529,19 +1547,20 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||||
|
|
||||||
// This is leveraging the fact that boost::signals2 executes connected
|
// This is leveraging the fact that boost::signals2 executes connected
|
||||||
// handlers in-order. Further up, the wallet is connected to this signal
|
// handlers in-order. Further up, the wallet is connected to this signal
|
||||||
// if the wallet is enabled. The wallet's ScriptForMining handler does
|
// if the wallet is enabled. The wallet's AddressForMining handler does
|
||||||
// nothing if -mineraddress is set, and GetScriptForMinerAddress() does
|
// nothing if -mineraddress is set, and GetMinerAddress() does nothing
|
||||||
// nothing if -mineraddress is not set (or set to an invalid address).
|
// if -mineraddress is not set (or set to an address that is not valid
|
||||||
|
// for mining).
|
||||||
//
|
//
|
||||||
// The upshot is that when ScriptForMining(script) is called:
|
// The upshot is that when AddressForMining(address) is called:
|
||||||
// - If -mineraddress is set (whether or not the wallet is enabled), the
|
// - If -mineraddress is set (whether or not the wallet is enabled), the
|
||||||
// CScript argument is set to -mineraddress.
|
// argument is set to -mineraddress.
|
||||||
// - If the wallet is enabled and -mineraddress is not set, the CScript
|
// - If the wallet is enabled and -mineraddress is not set, the argument
|
||||||
// argument is set to a wallet address.
|
// is set to a wallet address.
|
||||||
// - If the wallet is disabled and -mineraddress is not set, the CScript
|
// - If the wallet is disabled and -mineraddress is not set, the
|
||||||
// argument is not modified; in practice this means it is empty, and
|
// argument is not modified; in practice this means it is empty, and
|
||||||
// GenerateBitcoins() returns an error.
|
// GenerateBitcoins() returns an error.
|
||||||
GetMainSignals().ScriptForMining.connect(GetScriptForMinerAddress);
|
GetMainSignals().AddressForMining.connect(GetMinerAddress);
|
||||||
}
|
}
|
||||||
#endif // ENABLE_MINING
|
#endif // ENABLE_MINING
|
||||||
|
|
||||||
|
|
71
src/main.cpp
71
src/main.cpp
|
@ -791,6 +791,7 @@ bool ContextualCheckTransaction(
|
||||||
bool overwinterActive = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_OVERWINTER);
|
bool overwinterActive = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_OVERWINTER);
|
||||||
bool saplingActive = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_SAPLING);
|
bool saplingActive = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_SAPLING);
|
||||||
bool isSprout = !overwinterActive;
|
bool isSprout = !overwinterActive;
|
||||||
|
bool heartwoodActive = chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_HEARTWOOD);
|
||||||
|
|
||||||
// If Sprout rules apply, reject transactions which are intended for Overwinter and beyond
|
// If Sprout rules apply, reject transactions which are intended for Overwinter and beyond
|
||||||
if (isSprout && tx.fOverwintered) {
|
if (isSprout && tx.fOverwintered) {
|
||||||
|
@ -887,6 +888,54 @@ bool ContextualCheckTransaction(
|
||||||
REJECT_INVALID, "bad-txns-oversize");
|
REJECT_INVALID, "bad-txns-oversize");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rules that apply before Heartwood:
|
||||||
|
if (!heartwoodActive) {
|
||||||
|
if (tx.IsCoinBase()) {
|
||||||
|
// A coinbase transaction cannot have output descriptions
|
||||||
|
if (tx.vShieldedOutput.size() > 0)
|
||||||
|
return state.DoS(
|
||||||
|
dosLevelPotentiallyRelaxing,
|
||||||
|
error("CheckTransaction(): coinbase has output descriptions"),
|
||||||
|
REJECT_INVALID, "bad-cb-has-output-description");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rules that apply to Heartwood or later:
|
||||||
|
if (heartwoodActive) {
|
||||||
|
if (tx.IsCoinBase()) {
|
||||||
|
// All Sapling outputs in coinbase transactions MUST have valid note commitments
|
||||||
|
// when recovered using a 32-byte array of zeroes as the outgoing viewing key.
|
||||||
|
// https://zips.z.cash/zip-0213#specification
|
||||||
|
uint256 ovk;
|
||||||
|
for (const OutputDescription &output : tx.vShieldedOutput) {
|
||||||
|
auto outPlaintext = SaplingOutgoingPlaintext::decrypt(
|
||||||
|
output.outCiphertext, ovk, output.cv, output.cmu, output.ephemeralKey);
|
||||||
|
if (!outPlaintext) {
|
||||||
|
return state.DoS(
|
||||||
|
DOS_LEVEL_BLOCK,
|
||||||
|
error("CheckTransaction(): coinbase output description has invalid outCiphertext"),
|
||||||
|
REJECT_INVALID,
|
||||||
|
"bad-cb-output-desc-invalid-outct");
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaplingNotePlaintext::decrypt() checks note commitment validity.
|
||||||
|
if (!SaplingNotePlaintext::decrypt(
|
||||||
|
output.encCiphertext,
|
||||||
|
output.ephemeralKey,
|
||||||
|
outPlaintext->esk,
|
||||||
|
outPlaintext->pk_d,
|
||||||
|
output.cmu)
|
||||||
|
) {
|
||||||
|
return state.DoS(
|
||||||
|
DOS_LEVEL_BLOCK,
|
||||||
|
error("CheckTransaction(): coinbase output description has invalid encCiphertext"),
|
||||||
|
REJECT_INVALID,
|
||||||
|
"bad-cb-output-desc-invalid-encct");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint256 dataToBeSigned;
|
uint256 dataToBeSigned;
|
||||||
|
|
||||||
if (!tx.vJoinSplit.empty() ||
|
if (!tx.vJoinSplit.empty() ||
|
||||||
|
@ -1219,13 +1268,11 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
|
||||||
return state.DoS(100, error("CheckTransaction(): coinbase has joinsplits"),
|
return state.DoS(100, error("CheckTransaction(): coinbase has joinsplits"),
|
||||||
REJECT_INVALID, "bad-cb-has-joinsplits");
|
REJECT_INVALID, "bad-cb-has-joinsplits");
|
||||||
|
|
||||||
// A coinbase transaction cannot have spend descriptions or output descriptions
|
// A coinbase transaction cannot have spend descriptions
|
||||||
if (tx.vShieldedSpend.size() > 0)
|
if (tx.vShieldedSpend.size() > 0)
|
||||||
return state.DoS(100, error("CheckTransaction(): coinbase has spend descriptions"),
|
return state.DoS(100, error("CheckTransaction(): coinbase has spend descriptions"),
|
||||||
REJECT_INVALID, "bad-cb-has-spend-description");
|
REJECT_INVALID, "bad-cb-has-spend-description");
|
||||||
if (tx.vShieldedOutput.size() > 0)
|
// See ContextualCheckTransaction for consensus rules on coinbase output descriptions.
|
||||||
return state.DoS(100, error("CheckTransaction(): coinbase has output descriptions"),
|
|
||||||
REJECT_INVALID, "bad-cb-has-output-description");
|
|
||||||
|
|
||||||
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
|
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
|
||||||
return state.DoS(100, error("CheckTransaction(): coinbase script size"),
|
return state.DoS(100, error("CheckTransaction(): coinbase script size"),
|
||||||
|
@ -5305,8 +5352,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
||||||
// Reject incoming connections from nodes that don't know about the current epoch
|
// Reject incoming connections from nodes that don't know about the current epoch
|
||||||
const Consensus::Params& consensusParams = chainparams.GetConsensus();
|
const Consensus::Params& consensusParams = chainparams.GetConsensus();
|
||||||
auto currentEpoch = CurrentEpoch(GetHeight(), consensusParams);
|
auto currentEpoch = CurrentEpoch(GetHeight(), consensusParams);
|
||||||
if (pfrom->nVersion < consensusParams.vUpgrades[currentEpoch].nProtocolVersion)
|
if (pfrom->nVersion < consensusParams.vUpgrades[currentEpoch].nProtocolVersion &&
|
||||||
{
|
!(
|
||||||
|
chainparams.NetworkIDString() == "regtest" &&
|
||||||
|
!GetBoolArg("-nurejectoldversions", DEFAULT_NU_REJECT_OLD_VERSIONS)
|
||||||
|
)
|
||||||
|
) {
|
||||||
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
|
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
|
||||||
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
|
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
|
||||||
strprintf("Version must be %d or greater",
|
strprintf("Version must be %d or greater",
|
||||||
|
@ -5438,8 +5489,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
||||||
// 1. The version message has been received
|
// 1. The version message has been received
|
||||||
// 2. Peer version is below the minimum version for the current epoch
|
// 2. Peer version is below the minimum version for the current epoch
|
||||||
else if (pfrom->nVersion < chainparams.GetConsensus().vUpgrades[
|
else if (pfrom->nVersion < chainparams.GetConsensus().vUpgrades[
|
||||||
CurrentEpoch(GetHeight(), chainparams.GetConsensus())].nProtocolVersion)
|
CurrentEpoch(GetHeight(), chainparams.GetConsensus())].nProtocolVersion &&
|
||||||
{
|
!(
|
||||||
|
chainparams.NetworkIDString() == "regtest" &&
|
||||||
|
!GetBoolArg("-nurejectoldversions", DEFAULT_NU_REJECT_OLD_VERSIONS)
|
||||||
|
)
|
||||||
|
) {
|
||||||
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
|
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
|
||||||
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
|
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
|
||||||
strprintf("Version must be %d or greater",
|
strprintf("Version must be %d or greater",
|
||||||
|
|
|
@ -111,6 +111,9 @@ static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
|
||||||
static const bool DEFAULT_TXINDEX = false;
|
static const bool DEFAULT_TXINDEX = false;
|
||||||
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
|
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
|
||||||
|
|
||||||
|
/** Default for -nurejectoldversions */
|
||||||
|
static const bool DEFAULT_NU_REJECT_OLD_VERSIONS = true;
|
||||||
|
|
||||||
#define equihash_parameters_acceptable(N, K) \
|
#define equihash_parameters_acceptable(N, K) \
|
||||||
((CBlockHeader::HEADER_SIZE + equihash_solution_size(N, K))*MAX_HEADERS_RESULTS < \
|
((CBlockHeader::HEADER_SIZE + equihash_solution_size(N, K))*MAX_HEADERS_RESULTS < \
|
||||||
MAX_PROTOCOL_MESSAGE_LENGTH-1000)
|
MAX_PROTOCOL_MESSAGE_LENGTH-1000)
|
||||||
|
|
161
src/miner.cpp
161
src/miner.cpp
|
@ -26,11 +26,13 @@
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
#include "timedata.h"
|
#include "timedata.h"
|
||||||
|
#include "transaction_builder.h"
|
||||||
#include "ui_interface.h"
|
#include "ui_interface.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "utilmoneystr.h"
|
#include "utilmoneystr.h"
|
||||||
#include "validationinterface.h"
|
#include "validationinterface.h"
|
||||||
|
|
||||||
|
#include <librustzcash.h>
|
||||||
#include "sodium.h"
|
#include "sodium.h"
|
||||||
|
|
||||||
#include <boost/thread.hpp>
|
#include <boost/thread.hpp>
|
||||||
|
@ -113,7 +115,94 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn)
|
bool IsValidMinerAddress(const MinerAddress& minerAddr) {
|
||||||
|
return minerAddr.which() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AddOutputsToCoinbaseTxAndSign : public boost::static_visitor<>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
CMutableTransaction &mtx;
|
||||||
|
const CChainParams &chainparams;
|
||||||
|
const int nHeight;
|
||||||
|
const CAmount nFees;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AddOutputsToCoinbaseTxAndSign(
|
||||||
|
CMutableTransaction &mtx,
|
||||||
|
const CChainParams &chainparams,
|
||||||
|
const int nHeight,
|
||||||
|
const CAmount nFees) : mtx(mtx), chainparams(chainparams), nHeight(nHeight), nFees(nFees) {}
|
||||||
|
|
||||||
|
CAmount SetFoundersRewardAndGetMinerValue() const {
|
||||||
|
auto value = GetBlockSubsidy(nHeight, chainparams.GetConsensus());
|
||||||
|
|
||||||
|
if ((nHeight > 0) && (nHeight <= chainparams.GetConsensus().GetLastFoundersRewardBlockHeight(nHeight))) {
|
||||||
|
// Founders reward is 20% of the block subsidy
|
||||||
|
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 {}
|
||||||
|
|
||||||
|
// Create shielded output
|
||||||
|
void operator()(const libzcash::SaplingPaymentAddress &pa) const {
|
||||||
|
auto value = SetFoundersRewardAndGetMinerValue();
|
||||||
|
mtx.valueBalance = -value;
|
||||||
|
|
||||||
|
uint256 ovk;
|
||||||
|
auto note = libzcash::SaplingNote(pa, value);
|
||||||
|
auto output = OutputDescriptionInfo(ovk, note, {{0xF6}});
|
||||||
|
|
||||||
|
auto ctx = librustzcash_sapling_proving_ctx_init();
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
// Empty output script.
|
||||||
|
uint256 dataToBeSigned;
|
||||||
|
CScript scriptCode;
|
||||||
|
try {
|
||||||
|
dataToBeSigned = SignatureHash(
|
||||||
|
scriptCode, mtx, NOT_AN_INPUT, SIGHASH_ALL, 0,
|
||||||
|
CurrentEpochBranchId(nHeight, chainparams.GetConsensus()));
|
||||||
|
} catch (std::logic_error ex) {
|
||||||
|
librustzcash_sapling_proving_ctx_free(ctx);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
librustzcash_sapling_binding_sig(
|
||||||
|
ctx,
|
||||||
|
mtx.valueBalance,
|
||||||
|
dataToBeSigned.begin(),
|
||||||
|
mtx.bindingSig.data());
|
||||||
|
|
||||||
|
librustzcash_sapling_proving_ctx_free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create transparent output
|
||||||
|
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.
|
||||||
|
auto value = SetFoundersRewardAndGetMinerValue();
|
||||||
|
// Now fill in the miner's output.
|
||||||
|
mtx.vout[0].nValue = value;
|
||||||
|
mtx.vout[0].scriptPubKey = coinbaseScript->reserveScript;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddress& minerAddress)
|
||||||
{
|
{
|
||||||
// Create new block
|
// Create new block
|
||||||
std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
|
std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
|
||||||
|
@ -361,10 +450,6 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s
|
||||||
|
|
||||||
UpdateCoins(tx, view, nHeight);
|
UpdateCoins(tx, view, nHeight);
|
||||||
|
|
||||||
BOOST_FOREACH(const OutputDescription &outDescription, tx.vShieldedOutput) {
|
|
||||||
sapling_tree.append(outDescription.cmu);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Added
|
// Added
|
||||||
pblock->vtx.push_back(tx);
|
pblock->vtx.push_back(tx);
|
||||||
pblocktemplate->vTxFees.push_back(nTxFees);
|
pblocktemplate->vTxFees.push_back(nTxFees);
|
||||||
|
@ -406,29 +491,26 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s
|
||||||
CMutableTransaction txNew = CreateNewContextualCMutableTransaction(chainparams.GetConsensus(), nHeight);
|
CMutableTransaction txNew = CreateNewContextualCMutableTransaction(chainparams.GetConsensus(), nHeight);
|
||||||
txNew.vin.resize(1);
|
txNew.vin.resize(1);
|
||||||
txNew.vin[0].prevout.SetNull();
|
txNew.vin[0].prevout.SetNull();
|
||||||
txNew.vout.resize(1);
|
|
||||||
txNew.vout[0].scriptPubKey = scriptPubKeyIn;
|
|
||||||
txNew.vout[0].nValue = GetBlockSubsidy(nHeight, chainparams.GetConsensus());
|
|
||||||
// Set to 0 so expiry height does not apply to coinbase txs
|
// Set to 0 so expiry height does not apply to coinbase txs
|
||||||
txNew.nExpiryHeight = 0;
|
txNew.nExpiryHeight = 0;
|
||||||
|
|
||||||
if ((nHeight > 0) && (nHeight <= chainparams.GetConsensus().GetLastFoundersRewardBlockHeight(nHeight))) {
|
// Add outputs and sign
|
||||||
// Founders reward is 20% of the block subsidy
|
boost::apply_visitor(
|
||||||
auto vFoundersReward = txNew.vout[0].nValue / 5;
|
AddOutputsToCoinbaseTxAndSign(txNew, chainparams, nHeight, nFees),
|
||||||
// Take some reward away from us
|
minerAddress);
|
||||||
txNew.vout[0].nValue -= vFoundersReward;
|
|
||||||
|
|
||||||
// And give it to the founders
|
|
||||||
txNew.vout.push_back(CTxOut(vFoundersReward, chainparams.GetFoundersRewardScriptAtHeight(nHeight)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add fees
|
|
||||||
txNew.vout[0].nValue += nFees;
|
|
||||||
txNew.vin[0].scriptSig = CScript() << nHeight << OP_0;
|
txNew.vin[0].scriptSig = CScript() << nHeight << OP_0;
|
||||||
|
|
||||||
pblock->vtx[0] = txNew;
|
pblock->vtx[0] = txNew;
|
||||||
pblocktemplate->vTxFees[0] = -nFees;
|
pblocktemplate->vTxFees[0] = -nFees;
|
||||||
|
|
||||||
|
// Update the Sapling commitment tree.
|
||||||
|
for (const CTransaction& tx : pblock->vtx) {
|
||||||
|
for (const OutputDescription& odesc : tx.vShieldedOutput) {
|
||||||
|
sapling_tree.append(odesc.cmu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Randomise nonce
|
// Randomise nonce
|
||||||
arith_uint256 nonce = UintToArith256(GetRandHash());
|
arith_uint256 nonce = UintToArith256(GetRandHash());
|
||||||
// Clear the top and bottom 16 bits (for local use as thread flags and counters)
|
// Clear the top and bottom 16 bits (for local use as thread flags and counters)
|
||||||
|
@ -469,18 +551,26 @@ class MinerAddressScript : public CReserveScript
|
||||||
void KeepScript() {}
|
void KeepScript() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
void GetScriptForMinerAddress(boost::shared_ptr<CReserveScript> &script)
|
void GetMinerAddress(MinerAddress &minerAddress)
|
||||||
{
|
{
|
||||||
CTxDestination addr = DecodeDestination(GetArg("-mineraddress", ""));
|
// Try a transparent address first
|
||||||
if (!IsValidDestination(addr)) {
|
auto mAddrArg = GetArg("-mineraddress", "");
|
||||||
return;
|
CTxDestination addr = DecodeDestination(mAddrArg);
|
||||||
}
|
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);
|
||||||
|
|
||||||
script = mAddr;
|
mAddr->reserveScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||||
script->reserveScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
|
minerAddress = mAddr;
|
||||||
|
} else {
|
||||||
|
// Try a Sapling address
|
||||||
|
auto zaddr = DecodePaymentAddress(mAddrArg);
|
||||||
|
if (IsValidPaymentAddress(zaddr)) {
|
||||||
|
if (boost::get<libzcash::SaplingPaymentAddress>(&zaddr) != nullptr) {
|
||||||
|
minerAddress = boost::get<libzcash::SaplingPaymentAddress>(zaddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
|
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
|
||||||
|
@ -536,8 +626,8 @@ void static BitcoinMiner(const CChainParams& chainparams)
|
||||||
// Each thread has its own counter
|
// Each thread has its own counter
|
||||||
unsigned int nExtraNonce = 0;
|
unsigned int nExtraNonce = 0;
|
||||||
|
|
||||||
boost::shared_ptr<CReserveScript> coinbaseScript;
|
MinerAddress minerAddress;
|
||||||
GetMainSignals().ScriptForMining(coinbaseScript);
|
GetMainSignals().AddressForMining(minerAddress);
|
||||||
|
|
||||||
unsigned int n = chainparams.GetConsensus().nEquihashN;
|
unsigned int n = chainparams.GetConsensus().nEquihashN;
|
||||||
unsigned int k = chainparams.GetConsensus().nEquihashK;
|
unsigned int k = chainparams.GetConsensus().nEquihashK;
|
||||||
|
@ -557,9 +647,10 @@ void static BitcoinMiner(const CChainParams& chainparams)
|
||||||
miningTimer.start();
|
miningTimer.start();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//throw an error if no script was provided
|
// Throw an error if no address valid for mining was provided.
|
||||||
if (!coinbaseScript->reserveScript.size())
|
if (!IsValidMinerAddress(minerAddress)) {
|
||||||
throw std::runtime_error("No coinbase script available (mining requires a wallet or -mineraddress)");
|
throw std::runtime_error("No miner address available (mining requires a wallet or -mineraddress)");
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (chainparams.MiningRequiresPeers()) {
|
if (chainparams.MiningRequiresPeers()) {
|
||||||
|
@ -585,7 +676,7 @@ void static BitcoinMiner(const CChainParams& chainparams)
|
||||||
unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
|
unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
|
||||||
CBlockIndex* pindexPrev = chainActive.Tip();
|
CBlockIndex* pindexPrev = chainActive.Tip();
|
||||||
|
|
||||||
unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(chainparams, coinbaseScript->reserveScript));
|
unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(chainparams, minerAddress));
|
||||||
if (!pblocktemplate.get())
|
if (!pblocktemplate.get())
|
||||||
{
|
{
|
||||||
if (GetArg("-mineraddress", "").empty()) {
|
if (GetArg("-mineraddress", "").empty()) {
|
||||||
|
@ -633,7 +724,7 @@ void static BitcoinMiner(const CChainParams& chainparams)
|
||||||
solver, pblock->nNonce.ToString());
|
solver, pblock->nNonce.ToString());
|
||||||
|
|
||||||
std::function<bool(std::vector<unsigned char>)> validBlock =
|
std::function<bool(std::vector<unsigned char>)> validBlock =
|
||||||
[&pblock, &hashTarget, &chainparams, &m_cs, &cancelSolver, &coinbaseScript]
|
[&pblock, &hashTarget, &chainparams, &m_cs, &cancelSolver, &minerAddress]
|
||||||
(std::vector<unsigned char> soln) {
|
(std::vector<unsigned char> soln) {
|
||||||
// Write the solution to the hash and compute the result.
|
// Write the solution to the hash and compute the result.
|
||||||
LogPrint("pow", "- Checking solution against target\n");
|
LogPrint("pow", "- Checking solution against target\n");
|
||||||
|
@ -654,7 +745,7 @@ void static BitcoinMiner(const CChainParams& chainparams)
|
||||||
cancelSolver = false;
|
cancelSolver = false;
|
||||||
}
|
}
|
||||||
SetThreadPriority(THREAD_PRIORITY_LOWEST);
|
SetThreadPriority(THREAD_PRIORITY_LOWEST);
|
||||||
coinbaseScript->KeepScript();
|
boost::apply_visitor(KeepMinerAddress(), minerAddress);
|
||||||
|
|
||||||
// In regression test mode, stop mining after a block is found.
|
// In regression test mode, stop mining after a block is found.
|
||||||
if (chainparams.MineBlocksOnDemand()) {
|
if (chainparams.MineBlocksOnDemand()) {
|
||||||
|
|
28
src/miner.h
28
src/miner.h
|
@ -21,6 +21,28 @@ static const int DEFAULT_GENERATE_THREADS = 1;
|
||||||
|
|
||||||
static const bool DEFAULT_PRINTPRIORITY = false;
|
static const bool DEFAULT_PRINTPRIORITY = false;
|
||||||
|
|
||||||
|
class InvalidMinerAddress {
|
||||||
|
public:
|
||||||
|
friend bool operator==(const InvalidMinerAddress &a, const InvalidMinerAddress &b) { return true; }
|
||||||
|
friend bool operator<(const InvalidMinerAddress &a, const InvalidMinerAddress &b) { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef boost::variant<InvalidMinerAddress, libzcash::SaplingPaymentAddress, boost::shared_ptr<CReserveScript>> MinerAddress;
|
||||||
|
|
||||||
|
class KeepMinerAddress : public boost::static_visitor<>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KeepMinerAddress() {}
|
||||||
|
|
||||||
|
void operator()(const InvalidMinerAddress &invalid) const {}
|
||||||
|
void operator()(const libzcash::SaplingPaymentAddress &pa) const {}
|
||||||
|
void operator()(const boost::shared_ptr<CReserveScript> &coinbaseScript) const {
|
||||||
|
coinbaseScript->KeepScript();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool IsValidMinerAddress(const MinerAddress& minerAddr);
|
||||||
|
|
||||||
struct CBlockTemplate
|
struct CBlockTemplate
|
||||||
{
|
{
|
||||||
CBlock block;
|
CBlock block;
|
||||||
|
@ -29,11 +51,11 @@ struct CBlockTemplate
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Generate a new block, without valid proof-of-work */
|
/** Generate a new block, without valid proof-of-work */
|
||||||
CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn);
|
CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddress& minerAddress);
|
||||||
|
|
||||||
#ifdef ENABLE_MINING
|
#ifdef ENABLE_MINING
|
||||||
/** Get script for -mineraddress */
|
/** Get -mineraddress */
|
||||||
void GetScriptForMinerAddress(boost::shared_ptr<CReserveScript> &script);
|
void GetMinerAddress(MinerAddress &minerAddress);
|
||||||
/** Modify the extranonce in a block */
|
/** Modify the extranonce in a block */
|
||||||
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
|
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
|
||||||
/** Run the miner threads */
|
/** Run the miner threads */
|
||||||
|
|
|
@ -832,7 +832,12 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
|
||||||
{
|
{
|
||||||
// Find any nodes which don't support the protocol version for the next upgrade
|
// Find any nodes which don't support the protocol version for the next upgrade
|
||||||
for (const CNodeRef &node : vEvictionCandidates) {
|
for (const CNodeRef &node : vEvictionCandidates) {
|
||||||
if (node->nVersion < params.vUpgrades[idx].nProtocolVersion) {
|
if (node->nVersion < params.vUpgrades[idx].nProtocolVersion &&
|
||||||
|
!(
|
||||||
|
Params().NetworkIDString() == "regtest" &&
|
||||||
|
!GetBoolArg("-nurejectoldversions", DEFAULT_NU_REJECT_OLD_VERSIONS)
|
||||||
|
)
|
||||||
|
) {
|
||||||
vTmpEvictionCandidates.push_back(node);
|
vTmpEvictionCandidates.push_back(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,12 +183,13 @@ UniValue generate(const UniValue& params, bool fHelp)
|
||||||
int nHeight = 0;
|
int nHeight = 0;
|
||||||
int nGenerate = params[0].get_int();
|
int nGenerate = params[0].get_int();
|
||||||
|
|
||||||
boost::shared_ptr<CReserveScript> coinbaseScript;
|
MinerAddress minerAddress;
|
||||||
GetMainSignals().ScriptForMining(coinbaseScript);
|
GetMainSignals().AddressForMining(minerAddress);
|
||||||
|
|
||||||
//throw an error if no script was provided
|
// Throw an error if no address valid for mining was provided.
|
||||||
if (!coinbaseScript->reserveScript.size())
|
if (!IsValidMinerAddress(minerAddress)) {
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet or -mineraddress)");
|
throw JSONRPCError(RPC_INTERNAL_ERROR, "No miner address available (mining requires a wallet or -mineraddress)");
|
||||||
|
}
|
||||||
|
|
||||||
{ // Don't keep cs_main locked
|
{ // Don't keep cs_main locked
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
@ -202,7 +203,7 @@ UniValue generate(const UniValue& params, bool fHelp)
|
||||||
unsigned int k = Params().GetConsensus().nEquihashK;
|
unsigned int k = Params().GetConsensus().nEquihashK;
|
||||||
while (nHeight < nHeightEnd)
|
while (nHeight < nHeightEnd)
|
||||||
{
|
{
|
||||||
std::unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(Params(), coinbaseScript->reserveScript));
|
std::unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(Params(), minerAddress));
|
||||||
if (!pblocktemplate.get())
|
if (!pblocktemplate.get())
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
|
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
|
||||||
CBlock *pblock = &pblocktemplate->block;
|
CBlock *pblock = &pblocktemplate->block;
|
||||||
|
@ -255,8 +256,8 @@ endloop:
|
||||||
++nHeight;
|
++nHeight;
|
||||||
blockHashes.push_back(pblock->GetHash().GetHex());
|
blockHashes.push_back(pblock->GetHash().GetHex());
|
||||||
|
|
||||||
//mark script as important because it was used at least for one coinbase output
|
//mark miner address as important because it was used at least for one coinbase output
|
||||||
coinbaseScript->KeepScript();
|
boost::apply_visitor(KeepMinerAddress(), minerAddress);
|
||||||
}
|
}
|
||||||
return blockHashes;
|
return blockHashes;
|
||||||
}
|
}
|
||||||
|
@ -610,19 +611,20 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
|
||||||
pblocktemplate = NULL;
|
pblocktemplate = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::shared_ptr<CReserveScript> coinbaseScript;
|
MinerAddress minerAddress;
|
||||||
GetMainSignals().ScriptForMining(coinbaseScript);
|
GetMainSignals().AddressForMining(minerAddress);
|
||||||
|
|
||||||
// Throw an error if no script was provided
|
// Throw an error if no address valid for mining was provided.
|
||||||
if (!coinbaseScript->reserveScript.size())
|
if (!IsValidMinerAddress(minerAddress)) {
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet or -mineraddress)");
|
throw JSONRPCError(RPC_INTERNAL_ERROR, "No miner address available (mining requires a wallet or -mineraddress)");
|
||||||
|
}
|
||||||
|
|
||||||
pblocktemplate = CreateNewBlock(Params(), coinbaseScript->reserveScript);
|
pblocktemplate = CreateNewBlock(Params(), minerAddress);
|
||||||
if (!pblocktemplate)
|
if (!pblocktemplate)
|
||||||
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
|
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
|
||||||
|
|
||||||
// Mark script as important because it was used at least for one coinbase output
|
// Mark script as important because it was used at least for one coinbase output
|
||||||
coinbaseScript->KeepScript();
|
boost::apply_visitor(KeepMinerAddress(), minerAddress);
|
||||||
|
|
||||||
// Need to update only after we know CreateNewBlock succeeded
|
// Need to update only after we know CreateNewBlock succeeded
|
||||||
pindexPrev = pindexPrevNew;
|
pindexPrev = pindexPrevNew;
|
||||||
|
|
|
@ -135,11 +135,23 @@ struct {
|
||||||
{"00000000000000000000000000000000000000000000000000000000000022de", "007f388bed6b91756ea3e0866716ef6e9485fae6160195c7cda5c1e43f96ee359e105bcf4e8c293690420939124f04a0196363910421187811575929db40500b0bfdd1e8964aa334b801e3339a336d585a30852f1dc294a2d3d36f9ecc747458f3d41b4572415496df2a9fb1f882156cdabf9f65e681f38019865d6d47482277e24c9b8973eb34a41254faae4c5e2caa9dde5925ec118f3d8fa767ae00f434645957154367afe72000c59c79182c8faddd24424b9ebbb09ccd651b00540c96b9c7eec648a28a1d72c2e575d0f2250078511a011598db8e0788edf0ddc15ae24b62f63d6f93f71a2743a3c43ece55471a9802a76f31561a6f365c3647029bfa736395883afc0632bc25d4a8661b25d5aa0310f3c3fd3a183e75d359d6de3e5910b5dfbb74b7660af906917dc42b12e3e484aae1dbd20eaee037ef301572b7fc24d85b4aff9c82b27dcd421cee1639230d0188fec59f0dd4c0ced69c1ad07abd23692b1bd30735af942df597dcf6f403a36371bc416cf3e29a58570f586b05c357dc49515689788ad9581b8887dd913a41dc35e1ac9c9f9f4ea534eb6b36cc8af0299b6d3905750425da0366bdc59a7824477d7946b6f35c4ec90b8e61790fa74a4fa92396ea856661027828d40abb11dbe36bba516fe8ec8913106677285a4790d8034d1d1bf9fd87990889ddffc369b954a3d1c172be7e1812226c2b100cbe82c42bf4423456b6cb2bac3b4828135cb54f7a933a01f7f4a2057ad92136ba8e19fec313b412d43c089a71f06fd1625329b78d49ac92c59e4080932ddb1645910fd874dfb1f358e214231f62041acc41fd2c4e7b7127b3042459e1457f6b307fce9825aa4d2b942277f52665f2a77dd107b4f16cb3280f20c7551ff6cd855f97a6144131f69bab5648fb4b81261eefbf629094e8bcc4e36077f46d51a647da51fc01dca9a9ac12e2f7e2e2b1c9229dae099e95370177143d3b38ab661f19758494a01b32f0c27155b45a872a867dc50f9d76473695e9e2c4f9357f5ba6bb6c455d985f4e2c21486fde6576c6a8ceda6e010a7dc2b504130f429ac33376781ee4af5bbe8d768005bc4cb5092b15c4f296a8bd8c54a298eecd790a5161755a8605cc46bf890b8ff93d508501842b78c7261e5deeb1096891c528a300e57bf2f0aa9e8af2623cdf16bba20427704120484b6af8be26e4983d2685c783ce85d0174f84598719c6beefcc3603a94d4aa62750725df50671d7f9903ec255f779643ebd2fd8122fae3319e61928dcdaa44880d6a483140de63d2d7d7dc9dd449e0ee00d908e0f2164fc054198641e8fb0d74279c9b4117884b9335028a9f50c7223d3c03675ecf73329e52603f77f20cffba99356e51a365b75825f7db56d77542784f3c2663c493a2e564d73f753e9d6ebb0c2f2027a2330a7117c67a20507474fc47282a02cb572de17bbc7a335959316f74a05e3687cfb5227bc5b1b7f084f50902760e77740d420df9a495521c09b911e5f199a8343918b8386fc74f22552a76524a22c8c70ff06084e7fefe9b3ab98e004fadf35eb5f60483f287851712d90ebdd6b512877170d3b7fb34f16813917ae3b5ed54ede6081bdd7cc646fb336658121fd8fbafc52959b48d13375dfa4ce8616c157533a05ee1dc1120f215c348b54357d68adb4da7f5f48d55c005b3e7a23d05746e44d968f7601d4dbdff702861030d6e3a4140e6e1a29978be541f713f8e2cc9aa1ac32fecc941ee4aa4c41bc7f91ea5328ff87cbf35a8de17d1d3d1ad6d4384b0df52d10b3984d62e1678e86dd150ee425490bc727ee7107fda0f5d2433ab1c5d407be9d123fad5c201355601d926d3923787be86a4aa5be0b8d5750171ad658f8e97798b5dcaed46345a9af70c441"},
|
{"00000000000000000000000000000000000000000000000000000000000022de", "007f388bed6b91756ea3e0866716ef6e9485fae6160195c7cda5c1e43f96ee359e105bcf4e8c293690420939124f04a0196363910421187811575929db40500b0bfdd1e8964aa334b801e3339a336d585a30852f1dc294a2d3d36f9ecc747458f3d41b4572415496df2a9fb1f882156cdabf9f65e681f38019865d6d47482277e24c9b8973eb34a41254faae4c5e2caa9dde5925ec118f3d8fa767ae00f434645957154367afe72000c59c79182c8faddd24424b9ebbb09ccd651b00540c96b9c7eec648a28a1d72c2e575d0f2250078511a011598db8e0788edf0ddc15ae24b62f63d6f93f71a2743a3c43ece55471a9802a76f31561a6f365c3647029bfa736395883afc0632bc25d4a8661b25d5aa0310f3c3fd3a183e75d359d6de3e5910b5dfbb74b7660af906917dc42b12e3e484aae1dbd20eaee037ef301572b7fc24d85b4aff9c82b27dcd421cee1639230d0188fec59f0dd4c0ced69c1ad07abd23692b1bd30735af942df597dcf6f403a36371bc416cf3e29a58570f586b05c357dc49515689788ad9581b8887dd913a41dc35e1ac9c9f9f4ea534eb6b36cc8af0299b6d3905750425da0366bdc59a7824477d7946b6f35c4ec90b8e61790fa74a4fa92396ea856661027828d40abb11dbe36bba516fe8ec8913106677285a4790d8034d1d1bf9fd87990889ddffc369b954a3d1c172be7e1812226c2b100cbe82c42bf4423456b6cb2bac3b4828135cb54f7a933a01f7f4a2057ad92136ba8e19fec313b412d43c089a71f06fd1625329b78d49ac92c59e4080932ddb1645910fd874dfb1f358e214231f62041acc41fd2c4e7b7127b3042459e1457f6b307fce9825aa4d2b942277f52665f2a77dd107b4f16cb3280f20c7551ff6cd855f97a6144131f69bab5648fb4b81261eefbf629094e8bcc4e36077f46d51a647da51fc01dca9a9ac12e2f7e2e2b1c9229dae099e95370177143d3b38ab661f19758494a01b32f0c27155b45a872a867dc50f9d76473695e9e2c4f9357f5ba6bb6c455d985f4e2c21486fde6576c6a8ceda6e010a7dc2b504130f429ac33376781ee4af5bbe8d768005bc4cb5092b15c4f296a8bd8c54a298eecd790a5161755a8605cc46bf890b8ff93d508501842b78c7261e5deeb1096891c528a300e57bf2f0aa9e8af2623cdf16bba20427704120484b6af8be26e4983d2685c783ce85d0174f84598719c6beefcc3603a94d4aa62750725df50671d7f9903ec255f779643ebd2fd8122fae3319e61928dcdaa44880d6a483140de63d2d7d7dc9dd449e0ee00d908e0f2164fc054198641e8fb0d74279c9b4117884b9335028a9f50c7223d3c03675ecf73329e52603f77f20cffba99356e51a365b75825f7db56d77542784f3c2663c493a2e564d73f753e9d6ebb0c2f2027a2330a7117c67a20507474fc47282a02cb572de17bbc7a335959316f74a05e3687cfb5227bc5b1b7f084f50902760e77740d420df9a495521c09b911e5f199a8343918b8386fc74f22552a76524a22c8c70ff06084e7fefe9b3ab98e004fadf35eb5f60483f287851712d90ebdd6b512877170d3b7fb34f16813917ae3b5ed54ede6081bdd7cc646fb336658121fd8fbafc52959b48d13375dfa4ce8616c157533a05ee1dc1120f215c348b54357d68adb4da7f5f48d55c005b3e7a23d05746e44d968f7601d4dbdff702861030d6e3a4140e6e1a29978be541f713f8e2cc9aa1ac32fecc941ee4aa4c41bc7f91ea5328ff87cbf35a8de17d1d3d1ad6d4384b0df52d10b3984d62e1678e86dd150ee425490bc727ee7107fda0f5d2433ab1c5d407be9d123fad5c201355601d926d3923787be86a4aa5be0b8d5750171ad658f8e97798b5dcaed46345a9af70c441"},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Copied from src/miner.cpp
|
||||||
|
class MinerAddressScript : public CReserveScript
|
||||||
|
{
|
||||||
|
// CReserveScript requires implementing this function, so that if an
|
||||||
|
// internal (not-visible) wallet address is used, the wallet can mark it as
|
||||||
|
// important when a block is mined (so it then appears to the user).
|
||||||
|
// If -mineraddress is set, the user already knows about and is managing the
|
||||||
|
// address, so we don't need to do anything here.
|
||||||
|
void KeepScript() {}
|
||||||
|
};
|
||||||
|
|
||||||
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
|
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
|
||||||
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||||
{
|
{
|
||||||
const CChainParams& chainparams = Params(CBaseChainParams::MAIN);
|
const CChainParams& chainparams = Params(CBaseChainParams::MAIN);
|
||||||
CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
|
boost::shared_ptr<MinerAddressScript> scriptPubKey(new MinerAddressScript());
|
||||||
|
scriptPubKey->reserveScript = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
|
||||||
CBlockTemplate *pblocktemplate;
|
CBlockTemplate *pblocktemplate;
|
||||||
CMutableTransaction tx,tx2;
|
CMutableTransaction tx,tx2;
|
||||||
CScript script;
|
CScript script;
|
||||||
|
|
|
@ -153,7 +153,10 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>&
|
||||||
unsigned int n = chainparams.GetConsensus().nEquihashN;
|
unsigned int n = chainparams.GetConsensus().nEquihashN;
|
||||||
unsigned int k = chainparams.GetConsensus().nEquihashK;
|
unsigned int k = chainparams.GetConsensus().nEquihashK;
|
||||||
|
|
||||||
CBlockTemplate *pblocktemplate = CreateNewBlock(chainparams, scriptPubKey);
|
boost::shared_ptr<CReserveScript> mAddr(new CReserveScript());
|
||||||
|
mAddr->reserveScript = scriptPubKey;
|
||||||
|
|
||||||
|
CBlockTemplate *pblocktemplate = CreateNewBlock(chainparams, mAddr);
|
||||||
CBlock& block = pblocktemplate->block;
|
CBlock& block = pblocktemplate->block;
|
||||||
|
|
||||||
// Replace mempool-selected txns with just coinbase plus passed-in txns:
|
// Replace mempool-selected txns with just coinbase plus passed-in txns:
|
||||||
|
|
|
@ -451,11 +451,6 @@ void test_simple_sapling_invalidity(uint32_t consensusBranchId, CMutableTransact
|
||||||
vout.nValue = 1;
|
vout.nValue = 1;
|
||||||
newTx.vout.push_back(vout);
|
newTx.vout.push_back(vout);
|
||||||
|
|
||||||
newTx.vShieldedOutput.push_back(OutputDescription());
|
|
||||||
|
|
||||||
BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state));
|
|
||||||
BOOST_CHECK(state.GetRejectReason() == "bad-cb-has-output-description");
|
|
||||||
|
|
||||||
newTx.vShieldedSpend.push_back(SpendDescription());
|
newTx.vShieldedSpend.push_back(SpendDescription());
|
||||||
|
|
||||||
BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state));
|
BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state));
|
||||||
|
|
|
@ -22,6 +22,52 @@ SpendDescriptionInfo::SpendDescriptionInfo(
|
||||||
librustzcash_sapling_generate_r(alpha.begin());
|
librustzcash_sapling_generate_r(alpha.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::optional<OutputDescription> OutputDescriptionInfo::Build(void* ctx) {
|
||||||
|
auto cmu = this->note.cmu();
|
||||||
|
if (!cmu) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
libzcash::SaplingNotePlaintext notePlaintext(this->note, this->memo);
|
||||||
|
|
||||||
|
auto res = notePlaintext.encrypt(this->note.pk_d);
|
||||||
|
if (!res) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
auto enc = res.get();
|
||||||
|
auto encryptor = enc.second;
|
||||||
|
|
||||||
|
libzcash::SaplingPaymentAddress address(this->note.d, this->note.pk_d);
|
||||||
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
ss << address;
|
||||||
|
std::vector<unsigned char> addressBytes(ss.begin(), ss.end());
|
||||||
|
|
||||||
|
OutputDescription odesc;
|
||||||
|
if (!librustzcash_sapling_output_proof(
|
||||||
|
ctx,
|
||||||
|
encryptor.get_esk().begin(),
|
||||||
|
addressBytes.data(),
|
||||||
|
this->note.r.begin(),
|
||||||
|
this->note.value(),
|
||||||
|
odesc.cv.begin(),
|
||||||
|
odesc.zkproof.begin())) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
odesc.cmu = *cmu;
|
||||||
|
odesc.ephemeralKey = encryptor.get_epk();
|
||||||
|
odesc.encCiphertext = enc.first;
|
||||||
|
|
||||||
|
libzcash::SaplingOutgoingPlaintext outPlaintext(this->note.pk_d, encryptor.get_esk());
|
||||||
|
odesc.outCiphertext = outPlaintext.encrypt(
|
||||||
|
this->ovk,
|
||||||
|
odesc.cv,
|
||||||
|
odesc.cmu,
|
||||||
|
encryptor);
|
||||||
|
|
||||||
|
return odesc;
|
||||||
|
}
|
||||||
|
|
||||||
TransactionBuilderResult::TransactionBuilderResult(const CTransaction& tx) : maybeTx(tx) {}
|
TransactionBuilderResult::TransactionBuilderResult(const CTransaction& tx) : maybeTx(tx) {}
|
||||||
|
|
||||||
TransactionBuilderResult::TransactionBuilderResult(const std::string& error) : maybeError(error) {}
|
TransactionBuilderResult::TransactionBuilderResult(const std::string& error) : maybeError(error) {}
|
||||||
|
@ -302,51 +348,19 @@ TransactionBuilderResult TransactionBuilder::Build()
|
||||||
|
|
||||||
// Create Sapling OutputDescriptions
|
// Create Sapling OutputDescriptions
|
||||||
for (auto output : outputs) {
|
for (auto output : outputs) {
|
||||||
auto cmu = output.note.cmu();
|
// Check this out here as well to provide better logging.
|
||||||
if (!cmu) {
|
if (!output.note.cmu()) {
|
||||||
librustzcash_sapling_proving_ctx_free(ctx);
|
librustzcash_sapling_proving_ctx_free(ctx);
|
||||||
return TransactionBuilderResult("Output is invalid");
|
return TransactionBuilderResult("Output is invalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
libzcash::SaplingNotePlaintext notePlaintext(output.note, output.memo);
|
auto odesc = output.Build(ctx);
|
||||||
|
if (!odesc) {
|
||||||
auto res = notePlaintext.encrypt(output.note.pk_d);
|
|
||||||
if (!res) {
|
|
||||||
librustzcash_sapling_proving_ctx_free(ctx);
|
librustzcash_sapling_proving_ctx_free(ctx);
|
||||||
return TransactionBuilderResult("Failed to encrypt note");
|
return TransactionBuilderResult("Failed to create output description");
|
||||||
}
|
|
||||||
auto enc = res.get();
|
|
||||||
auto encryptor = enc.second;
|
|
||||||
|
|
||||||
libzcash::SaplingPaymentAddress address(output.note.d, output.note.pk_d);
|
|
||||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
|
||||||
ss << address;
|
|
||||||
std::vector<unsigned char> addressBytes(ss.begin(), ss.end());
|
|
||||||
|
|
||||||
OutputDescription odesc;
|
|
||||||
if (!librustzcash_sapling_output_proof(
|
|
||||||
ctx,
|
|
||||||
encryptor.get_esk().begin(),
|
|
||||||
addressBytes.data(),
|
|
||||||
output.note.r.begin(),
|
|
||||||
output.note.value(),
|
|
||||||
odesc.cv.begin(),
|
|
||||||
odesc.zkproof.begin())) {
|
|
||||||
librustzcash_sapling_proving_ctx_free(ctx);
|
|
||||||
return TransactionBuilderResult("Output proof failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
odesc.cmu = *cmu;
|
mtx.vShieldedOutput.push_back(odesc.get());
|
||||||
odesc.ephemeralKey = encryptor.get_epk();
|
|
||||||
odesc.encCiphertext = enc.first;
|
|
||||||
|
|
||||||
libzcash::SaplingOutgoingPlaintext outPlaintext(output.note.pk_d, encryptor.get_esk());
|
|
||||||
odesc.outCiphertext = outPlaintext.encrypt(
|
|
||||||
output.ovk,
|
|
||||||
odesc.cv,
|
|
||||||
odesc.cmu,
|
|
||||||
encryptor);
|
|
||||||
mtx.vShieldedOutput.push_back(odesc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -43,6 +43,8 @@ struct OutputDescriptionInfo {
|
||||||
uint256 ovk,
|
uint256 ovk,
|
||||||
libzcash::SaplingNote note,
|
libzcash::SaplingNote note,
|
||||||
std::array<unsigned char, ZC_MEMO_SIZE> memo) : ovk(ovk), note(note), memo(memo) {}
|
std::array<unsigned char, ZC_MEMO_SIZE> memo) : ovk(ovk), note(note), memo(memo) {}
|
||||||
|
|
||||||
|
boost::optional<OutputDescription> Build(void* ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TransparentInputInfo {
|
struct TransparentInputInfo {
|
||||||
|
|
|
@ -230,6 +230,27 @@ void RegtestDeactivateBlossom() {
|
||||||
SelectParams(CBaseChainParams::MAIN);
|
SelectParams(CBaseChainParams::MAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Consensus::Params& RegtestActivateHeartwood(bool updatePow, int heartwoodActivationHeight) {
|
||||||
|
SelectParams(CBaseChainParams::REGTEST);
|
||||||
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||||
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||||
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_BLOSSOM, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||||
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_HEARTWOOD, heartwoodActivationHeight);
|
||||||
|
if (updatePow) {
|
||||||
|
UpdateRegtestPow(32, 16, uint256S("0007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
|
||||||
|
}
|
||||||
|
return Params().GetConsensus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegtestDeactivateHeartwood() {
|
||||||
|
UpdateRegtestPow(0, 0, uint256S("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"));
|
||||||
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_HEARTWOOD, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||||
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_BLOSSOM, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||||
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||||
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||||
|
SelectParams(CBaseChainParams::MAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
libzcash::SaplingExtendedSpendingKey GetTestMasterSaplingSpendingKey() {
|
libzcash::SaplingExtendedSpendingKey GetTestMasterSaplingSpendingKey() {
|
||||||
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
|
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
|
||||||
|
|
|
@ -49,6 +49,10 @@ const Consensus::Params& RegtestActivateBlossom(bool updatePow, int blossomActiv
|
||||||
|
|
||||||
void RegtestDeactivateBlossom();
|
void RegtestDeactivateBlossom();
|
||||||
|
|
||||||
|
const Consensus::Params& RegtestActivateHeartwood(bool updatePow, int heartwoodActivationHeight = Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||||
|
|
||||||
|
void RegtestDeactivateHeartwood();
|
||||||
|
|
||||||
libzcash::SaplingExtendedSpendingKey GetTestMasterSaplingSpendingKey();
|
libzcash::SaplingExtendedSpendingKey GetTestMasterSaplingSpendingKey();
|
||||||
|
|
||||||
CKey AddTestCKeyToKeyStore(CBasicKeyStore& keyStore);
|
CKey AddTestCKeyToKeyStore(CBasicKeyStore& keyStore);
|
||||||
|
|
|
@ -33,13 +33,13 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
|
||||||
g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
|
g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
|
||||||
g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1));
|
g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1));
|
||||||
g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
|
g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
|
||||||
g_signals.ScriptForMining.connect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1));
|
g_signals.AddressForMining.connect(boost::bind(&CValidationInterface::GetAddressForMining, pwalletIn, _1));
|
||||||
g_signals.BlockFound.connect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
|
g_signals.BlockFound.connect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
|
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
|
||||||
g_signals.BlockFound.disconnect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
|
g_signals.BlockFound.disconnect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
|
||||||
g_signals.ScriptForMining.disconnect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1));
|
g_signals.AddressForMining.disconnect(boost::bind(&CValidationInterface::GetAddressForMining, pwalletIn, _1));
|
||||||
g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
|
g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
|
||||||
g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1));
|
g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1));
|
||||||
g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
|
g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
|
||||||
|
@ -53,7 +53,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
|
||||||
|
|
||||||
void UnregisterAllValidationInterfaces() {
|
void UnregisterAllValidationInterfaces() {
|
||||||
g_signals.BlockFound.disconnect_all_slots();
|
g_signals.BlockFound.disconnect_all_slots();
|
||||||
g_signals.ScriptForMining.disconnect_all_slots();
|
g_signals.AddressForMining.disconnect_all_slots();
|
||||||
g_signals.BlockChecked.disconnect_all_slots();
|
g_signals.BlockChecked.disconnect_all_slots();
|
||||||
g_signals.Broadcast.disconnect_all_slots();
|
g_signals.Broadcast.disconnect_all_slots();
|
||||||
g_signals.Inventory.disconnect_all_slots();
|
g_signals.Inventory.disconnect_all_slots();
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <boost/signals2/signal.hpp>
|
#include <boost/signals2/signal.hpp>
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
#include "miner.h"
|
||||||
#include "zcash/IncrementalMerkleTree.hpp"
|
#include "zcash/IncrementalMerkleTree.hpp"
|
||||||
|
|
||||||
class CBlock;
|
class CBlock;
|
||||||
|
@ -40,7 +41,7 @@ protected:
|
||||||
virtual void Inventory(const uint256 &hash) {}
|
virtual void Inventory(const uint256 &hash) {}
|
||||||
virtual void ResendWalletTransactions(int64_t nBestBlockTime) {}
|
virtual void ResendWalletTransactions(int64_t nBestBlockTime) {}
|
||||||
virtual void BlockChecked(const CBlock&, const CValidationState&) {}
|
virtual void BlockChecked(const CBlock&, const CValidationState&) {}
|
||||||
virtual void GetScriptForMining(boost::shared_ptr<CReserveScript>&) {};
|
virtual void GetAddressForMining(MinerAddress&) {};
|
||||||
virtual void ResetRequestCount(const uint256 &hash) {};
|
virtual void ResetRequestCount(const uint256 &hash) {};
|
||||||
friend void ::RegisterValidationInterface(CValidationInterface*);
|
friend void ::RegisterValidationInterface(CValidationInterface*);
|
||||||
friend void ::UnregisterValidationInterface(CValidationInterface*);
|
friend void ::UnregisterValidationInterface(CValidationInterface*);
|
||||||
|
@ -66,8 +67,8 @@ struct CMainSignals {
|
||||||
boost::signals2::signal<void (int64_t nBestBlockTime)> Broadcast;
|
boost::signals2::signal<void (int64_t nBestBlockTime)> Broadcast;
|
||||||
/** Notifies listeners of a block validation result */
|
/** Notifies listeners of a block validation result */
|
||||||
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
|
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
|
||||||
/** Notifies listeners that a key for mining is required (coinbase) */
|
/** Notifies listeners that an address for mining is required (coinbase) */
|
||||||
boost::signals2::signal<void (boost::shared_ptr<CReserveScript>&)> ScriptForMining;
|
boost::signals2::signal<void (MinerAddress&)> AddressForMining;
|
||||||
/** Notifies listeners that a block has been successfully mined */
|
/** Notifies listeners that a block has been successfully mined */
|
||||||
boost::signals2::signal<void (const uint256 &)> BlockFound;
|
boost::signals2::signal<void (const uint256 &)> BlockFound;
|
||||||
};
|
};
|
||||||
|
|
|
@ -4245,7 +4245,7 @@ void CWallet::UpdatedTransaction(const uint256 &hashTx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWallet::GetScriptForMining(boost::shared_ptr<CReserveScript> &script)
|
void CWallet::GetAddressForMining(MinerAddress &minerAddress)
|
||||||
{
|
{
|
||||||
if (!GetArg("-mineraddress", "").empty()) {
|
if (!GetArg("-mineraddress", "").empty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -4256,8 +4256,8 @@ void CWallet::GetScriptForMining(boost::shared_ptr<CReserveScript> &script)
|
||||||
if (!rKey->GetReservedKey(pubkey))
|
if (!rKey->GetReservedKey(pubkey))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
script = rKey;
|
rKey->reserveScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||||
script->reserveScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
|
minerAddress = rKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWallet::LockCoin(COutPoint& output)
|
void CWallet::LockCoin(COutPoint& output)
|
||||||
|
@ -4885,12 +4885,16 @@ void CWallet::GetFilteredNotes(
|
||||||
|
|
||||||
// Filter the transactions before checking for notes
|
// Filter the transactions before checking for notes
|
||||||
if (!CheckFinalTx(wtx) ||
|
if (!CheckFinalTx(wtx) ||
|
||||||
wtx.GetBlocksToMaturity() > 0 ||
|
|
||||||
wtx.GetDepthInMainChain() < minDepth ||
|
wtx.GetDepthInMainChain() < minDepth ||
|
||||||
wtx.GetDepthInMainChain() > maxDepth) {
|
wtx.GetDepthInMainChain() > maxDepth) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter coinbase transactions that don't have Sapling outputs
|
||||||
|
if (wtx.IsCoinBase() && wtx.mapSaplingNoteData.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto & pair : wtx.mapSproutNoteData) {
|
for (auto & pair : wtx.mapSproutNoteData) {
|
||||||
JSOutPoint jsop = pair.first;
|
JSOutPoint jsop = pair.first;
|
||||||
SproutNoteData nd = pair.second;
|
SproutNoteData nd = pair.second;
|
||||||
|
|
|
@ -1268,7 +1268,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetScriptForMining(boost::shared_ptr<CReserveScript> &script);
|
void GetAddressForMining(MinerAddress &minerAddress);
|
||||||
void ResetRequestCount(const uint256 &hash)
|
void ResetRequestCount(const uint256 &hash)
|
||||||
{
|
{
|
||||||
LOCK(cs_wallet);
|
LOCK(cs_wallet);
|
||||||
|
|
Loading…
Reference in New Issue