Skip to content

Commit 5f77a5f

Browse files
Place block signing pubkey in coinbase
1 parent f6d72a5 commit 5f77a5f

File tree

5 files changed

+114
-38
lines changed

5 files changed

+114
-38
lines changed

src/miner.cpp

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -511,20 +511,25 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda
511511
}
512512
}
513513

514-
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
514+
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce, const CPubKey* signingPubKey)
515515
{
516516
// Update nExtraNonce
517-
static uint256 hashPrevBlock;
518-
if (hashPrevBlock != pblock->hashPrevBlock)
519-
{
520-
if (!pblock->IsProofOfStake())
517+
if (!pblock->IsProofOfStake()) {
518+
static uint256 hashPrevBlock;
519+
if (hashPrevBlock != pblock->hashPrevBlock)
520+
{
521521
nExtraNonce = 0;
522-
hashPrevBlock = pblock->hashPrevBlock;
522+
hashPrevBlock = pblock->hashPrevBlock;
523+
}
523524
}
524525
++nExtraNonce;
525526
unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
526527
CMutableTransaction txCoinbase(*pblock->vtx[0]);
527528
txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce));
529+
if (signingPubKey) {
530+
txCoinbase.vin[0].scriptSig << ToByteVector(*signingPubKey);
531+
//txCoinbase.vout.push_back(CTxOut(0, CScript() << OP_RETURN << ToByteVector(*signingPubKey)));
532+
}
528533
assert(txCoinbase.vin[0].scriptSig.size() <= 100);
529534

530535
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
@@ -591,13 +596,20 @@ bool CreateCoinStake(CMutableTransaction& coinstakeTx, CBlock* pblock, std::shar
591596
if (gArgs.GetBoolArg("-debug", false) && gArgs.GetBoolArg("-printcoinstake", false))
592597
LogPrintf("%s : parsed kernel type=%s\n", __func__, GetTxnOutputType(whichType));
593598

594-
if (whichType == TxoutType::PUBKEYHASH || whichType == TxoutType::WITNESS_V0_KEYHASH || whichType == TxoutType::SCRIPTHASH) { // we support p2pkh, p2wpkh, and p2sh-p2wpkh inputs
595-
if (whichType == TxoutType::SCRIPTHASH) { // a p2sh input could be many things, but we only support p2sh-p2wpkh for now
599+
if (whichType == TxoutType::PUBKEY || whichType == TxoutType::PUBKEYHASH || whichType == TxoutType::WITNESS_V0_KEYHASH || whichType == TxoutType::SCRIPTHASH || whichType == TxoutType::WITNESS_V0_SCRIPTHASH) { // we support p2pkh, p2wpkh, p2sh-p2wpkh, and p2sh/p2wsh-multisig inputs
600+
const bool fNewStakingCodeActive = Params().NetworkIDString() != CBaseChainParams::MAIN;
601+
if (whichType == TxoutType::SCRIPTHASH || whichType == TxoutType::WITNESS_V0_SCRIPTHASH) { // a p2sh/p2wsh input could be many things, but we only support p2sh-p2wpkh and multisig for now
596602
CScript subscript;
597603
std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKeyKernel);
598-
if (provider && provider->GetCScript(CScriptID(uint160(vSolutions[0])), subscript)) { // extract the redeem script
604+
uint160 hash;
605+
if (whichType == TxoutType::WITNESS_V0_SCRIPTHASH) {
606+
CRIPEMD160 hasher;
607+
hasher.Write(&vSolutions[0][0], 32).Finalize(hash.begin());
608+
} else // whichType == TxoutType::SCRIPTHASH
609+
hash = uint160(vSolutions[0]);
610+
if (provider && provider->GetCScript(CScriptID(hash), subscript)) { // extract the redeem script
599611
TxoutType scriptType = Solver(subscript, vSolutions);
600-
if (scriptType != TxoutType::WITNESS_V0_KEYHASH || Params().NetworkIDString() == CBaseChainParams::MAIN) { // this is a script we don't recognize
612+
if (!fNewStakingCodeActive || (scriptType != TxoutType::WITNESS_V0_KEYHASH && scriptType != TxoutType::MULTISIG && scriptType != TxoutType::MULTISIG_DATA)) { // this is a script we don't recognize
601613
if (gArgs.GetBoolArg("-debug", false) && gArgs.GetBoolArg("-printcoinstake", false))
602614
LogPrintf("%s : no support for %s kernel type=%s\n", __func__, GetTxnOutputType(whichType), GetTxnOutputType(scriptType));
603615
continue;
@@ -610,7 +622,7 @@ bool CreateCoinStake(CMutableTransaction& coinstakeTx, CBlock* pblock, std::shar
610622
}
611623
}
612624

613-
if (gArgs.GetBoolArg("-quantumsafestaking", false)) { // a new bech32 address is generated for every stake to protect the public key from quantum computers
625+
if ((fNewStakingCodeActive || whichType != TxoutType::PUBKEY) && gArgs.GetBoolArg("-quantumsafestaking", false)) { // a new bech32 address is generated for every stake to protect the public key from quantum computers
614626
OutputType output_type = OutputType::BECH32;
615627
CTxDestination dest;
616628
std::string error;
@@ -621,7 +633,18 @@ bool CreateCoinStake(CMutableTransaction& coinstakeTx, CBlock* pblock, std::shar
621633
LogPrintf("%s : failed to get new destination for coinstake (%s)\n", __func__, error);
622634
scriptPubKeyOut = scriptPubKeyKernel;
623635
}
624-
} else if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { // on legacy wallets we can convert every input to p2pk for smaller coinstake TXs
636+
} else if (whichType == TxoutType::MULTISIG || whichType == TxoutType::MULTISIG_DATA) { // try to create a new destination for p2sh/p2wsh-multisig inputs
637+
OutputType output_type = OutputType::BECH32;
638+
CTxDestination dest;
639+
std::string error;
640+
if (pwallet->GetNewChangeDestination(output_type, dest, error)) {
641+
LogPrintf("%s : using new destination for coinstake (%s)\n", __func__, EncodeDestination(dest));
642+
scriptPubKeyOut = GetScriptForDestination(dest);
643+
} else
644+
continue;
645+
} else if (pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) || whichType == TxoutType::PUBKEY) { // descriptor wallets only credit earnings back to the original address and p2pk inputs can be left alone
646+
scriptPubKeyOut = scriptPubKeyKernel;
647+
} else { // on legacy wallets we can convert every input to p2pk for smaller coinstake TXs
625648
// convert to pay to public key type
626649
CPubKey pubkey;
627650
std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKeyKernel);
@@ -631,11 +654,7 @@ bool CreateCoinStake(CMutableTransaction& coinstakeTx, CBlock* pblock, std::shar
631654
continue; // unable to find corresponding public key
632655
}
633656
scriptPubKeyOut << ToByteVector(pubkey) << OP_CHECKSIG;
634-
} else { // descriptor wallets only credit earnings back to the original address
635-
scriptPubKeyOut = scriptPubKeyKernel;
636657
}
637-
} else if (whichType == TxoutType::PUBKEY) { // p2pk inputs can be left alone
638-
scriptPubKeyOut = scriptPubKeyKernel;
639658
} else if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) && (whichType == TxoutType::MULTISIG || whichType == TxoutType::MULTISIG_DATA)) { // convert multisig to p2pk
640659
// convert to pay to public key type
641660
CPubKey pubkey;
@@ -655,7 +674,7 @@ bool CreateCoinStake(CMutableTransaction& coinstakeTx, CBlock* pblock, std::shar
655674
} else {
656675
if (gArgs.GetBoolArg("-debug", false) && gArgs.GetBoolArg("-printcoinstake", false))
657676
LogPrintf("%s : no support for kernel type=%s\n", __func__, GetTxnOutputType(whichType));
658-
continue; // only support p2pk, p2pkh, p2wpkh, and p2sh-p2wpkh
677+
continue; // only support p2pk, p2pkh, p2wpkh, p2sh-p2wpkh, and p2sh/p2wsh-multisig
659678
}
660679

661680
coinstakeTx.vin.push_back(CTxIn(prevout.hash, prevout.n));
@@ -693,8 +712,9 @@ bool CreateCoinStake(CMutableTransaction& coinstakeTx, CBlock* pblock, std::shar
693712

694713
static inline bool ProcessBlockFound(const CBlock* pblock, const CChainParams& chainparams, ChainstateManager* chainman)
695714
{
696-
LogPrintf("%s\n", pblock->ToString());
697-
LogPrintf("generated %s\n", FormatMoney(pblock->IsProofOfStake() ? pblock->vtx[1]->GetValueOut() : pblock->vtx[0]->GetValueOut()));
715+
if (chainparams.NetworkIDString() != CBaseChainParams::REGTEST)
716+
LogPrintf("%s", pblock->ToString());
717+
//LogPrintf("generated %s\n", FormatMoney(pblock->IsProofOfStake() ? pblock->vtx[1]->GetValueOut() : pblock->vtx[0]->GetValueOut()));
698718

699719
// Found a solution
700720
{
@@ -813,12 +833,21 @@ static inline void PoSMiner(std::shared_ptr<CWallet> pwallet, ChainstateManager*
813833
return;
814834
}
815835
pblock = &pblocktemplate->block;
816-
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
836+
CPubKey signingPubKey;
837+
bool pubkeyInSig = true;
838+
{
839+
LOCK(pwallet->cs_wallet);
840+
if (!pwallet->GetBlockSigningPubKey(*pblock, signingPubKey, pubkeyInSig)) {
841+
LogPrintf("PoSMiner(): failed to get signing pubkey for PoS block\n");
842+
continue;
843+
}
844+
}
845+
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, pubkeyInSig ? nullptr : &signingPubKey);
817846

818847
// peercoin: if proof-of-stake block found then process block
819848
{
820849
LOCK(pwallet->cs_wallet);
821-
if (!pwallet->SignBlock(*pblock)) {
850+
if (!pwallet->SignBlock(*pblock, signingPubKey)) {
822851
LogPrintf("PoSMiner(): failed to sign PoS block\n");
823852
continue;
824853
}

src/miner.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <optional.h>
1010
#include <primitives/block.h>
11+
#include <pubkey.h>
1112
#include <txmempool.h>
1213
#include <validation.h>
1314

@@ -210,7 +211,7 @@ class BlockAssembler
210211
};
211212

212213
/** Modify the extranonce in a block */
213-
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
214+
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce, const CPubKey* signingPubKey = nullptr);
214215
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
215216

216217
/** Update an old GenerateCoinbaseCommitment from CreateNewBlock after the block txs have changed */

src/validation.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5731,28 +5731,39 @@ bool CheckBlockSignature(const CBlock& block)
57315731
if (block.vchBlockSig.empty())
57325732
return error("%s : vchBlockSig is empty!", __func__);
57335733

5734+
const bool fProofOfStake = block.IsProofOfStake();
57345735
std::vector<std::vector<unsigned char>> vSolutions;
5735-
const CTxOut& txout = block.IsProofOfStake() ? block.vtx[1]->vout[1] : block.vtx[0]->vout[0];
5736+
const CTxOut& txout = fProofOfStake ? block.vtx[1]->vout[1] : block.vtx[0]->vout[0];
57365737
TxoutType whichType = Solver(txout.scriptPubKey, vSolutions);
57375738
CPubKey pubkey;
57385739

57395740
if (whichType == TxoutType::PUBKEY) {
57405741
pubkey = CPubKey(vSolutions[0]);
57415742
} else {
5742-
if (block.IsProofOfWork()) // no inputs for coinbase
5743-
return false;
5744-
const CTxIn& txin = block.vtx[1]->vin[0];
5745-
if ((txin.scriptSig.size() == 0 || txin.scriptSig.size() == 23) && txin.scriptWitness.stack.size() == 2 && txin.scriptWitness.stack.back().size() == CPubKey::COMPRESSED_SIZE) { // p2wpkh or p2sh-p2wpkh input
5743+
const CTxIn& cbtxin = block.vtx[0]->vin[0];
5744+
const CTxIn& txin = fProofOfStake ? block.vtx[1]->vin[0] : cbtxin;
5745+
if (fProofOfStake && (txin.scriptSig.size() == 0 || txin.scriptSig.size() == 23) && txin.scriptWitness.stack.size() == 2 && txin.scriptWitness.stack.back().size() == CPubKey::COMPRESSED_SIZE) { // p2wpkh or p2sh-p2wpkh input
57465746
pubkey = CPubKey(txin.scriptWitness.stack.back());
5747-
} else if (txin.scriptWitness.stack.size() == 0 && txin.scriptSig.size() >= 70 && txin.scriptSig.size() <= 140 && txin.scriptSig.IsPushOnly() && txin.scriptSig[0] >= 70 && txin.scriptSig[0] <= 73) { // p2pkh input (sig + pubkey)
5747+
} else if (fProofOfStake && txin.scriptWitness.stack.size() == 0 && txin.scriptSig.size() >= 100 && txin.scriptSig.size() <= 140 && txin.scriptSig.IsPushOnly() && txin.scriptSig[0] >= 70 && txin.scriptSig[0] <= 73) { // p2pkh input (sig + pubkey)
57485748
unsigned int pubkeyStart = txin.scriptSig[0] + 2u; // skip sig and length bytes by reading sig length from pushdata
57495749
//LogPrintf("%s : p2pkh txin.scriptSig = %s\n", __func__, HexStr(txin.scriptSig));
57505750
//LogPrintf("%s : txin.scriptSig.size() = %u, txin.scriptSig[0] = %u, pubkeyStart = %u\n", __func__, txin.scriptSig.size(), txin.scriptSig[0], pubkeyStart);
57515751
if ((pubkeyStart + CPubKey::COMPRESSED_SIZE) > txin.scriptSig.size() || txin.scriptSig[pubkeyStart-1] < CPubKey::COMPRESSED_SIZE) // last pushdata must be large enough to at least hold a compressed pubkey
5752-
return false;
5752+
return error("%s : p2pkh txin.scriptSig.size() = %u is too small", __func__, txin.scriptSig.size());
57535753
pubkey = CPubKey(txin.scriptSig.begin()+pubkeyStart, txin.scriptSig.end());
5754+
} else if (cbtxin.scriptSig.size() > CPubKey::COMPRESSED_SIZE) { // check for pubkey in coinbase
5755+
//std::vector<unsigned char> vchPubKey(cbtxin.scriptSig.end()-CPubKey::COMPRESSED_SIZE, cbtxin.scriptSig.end());
5756+
//LogPrintf("%s : coinbase cbtxin.scriptSig = %s\n", __func__, HexStr(cbtxin.scriptSig));
5757+
//LogPrintf("%s : cbtxin.scriptSig.size() = %u, vchPubKey = %s\n", __func__, cbtxin.scriptSig.size(), HexStr(vchPubKey));
5758+
pubkey = CPubKey(cbtxin.scriptSig.end()-CPubKey::COMPRESSED_SIZE, cbtxin.scriptSig.end());
5759+
if (whichType == TxoutType::PUBKEYHASH || whichType == TxoutType::WITNESS_V0_KEYHASH) { // this code could be removed in the future to allow block signing with any arbitrary pubkey
5760+
if (Hash160(pubkey) != uint160(vSolutions[0])) {
5761+
return error("%s : pubkey used for block signature (%s) does not correspond to first output", __func__, HexStr(pubkey));
5762+
}
5763+
} else
5764+
return error("%s : unable to verify pubkey belongs to first output of type=%s", __func__, GetTxnOutputType(whichType));
57545765
} else { // we don't know where the pubkey is or how to parse it
5755-
return false;
5766+
return error("%s : unable to find pubkey to validate block signature", __func__);
57565767
}
57575768
}
57585769
//LogPrintf("%s : validating signature for %s output using pubkey %s\n", __func__, GetTxnOutputType(whichType), HexStr(pubkey));

src/wallet/wallet.cpp

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4629,7 +4629,7 @@ bool CWallet::SelectStakeCoins(std::set<CInputCoin>& setCoins) const
46294629
}
46304630

46314631
// peercoin: sign block
4632-
bool CWallet::SignBlock(CBlock& block) const
4632+
bool CWallet::GetBlockSigningPubKey(const CBlock& block, CPubKey& pubkey, bool& pubkeyInSig) const
46334633
{
46344634
AssertLockHeld(cs_wallet);
46354635

@@ -4649,20 +4649,26 @@ bool CWallet::SignBlock(CBlock& block) const
46494649
}
46504650
TxoutType whichType = Solver(scriptPubKey, vSolutions);
46514651

4652-
// Sign
4653-
CPubKey pubkey;
46544652
if (whichType == TxoutType::PUBKEY) {
46554653
pubkey = CPubKey(vSolutions[0]);
46564654
} else if (whichType == TxoutType::PUBKEYHASH || whichType == TxoutType::WITNESS_V0_KEYHASH) {
46574655
std::unique_ptr<SigningProvider> provider = GetSolvingProvider(scriptPubKey);
46584656
if (!provider || !provider->GetPubKey(CKeyID(uint160(vSolutions[0])), pubkey))
46594657
return false;
4660-
} else if (whichType == TxoutType::SCRIPTHASH) {
4658+
} else if (whichType == TxoutType::SCRIPTHASH || whichType == TxoutType::WITNESS_V0_SCRIPTHASH) {
46614659
CScript subscript;
46624660
std::unique_ptr<SigningProvider> provider = GetSolvingProvider(scriptPubKey);
4663-
if (provider && provider->GetCScript(CScriptID(uint160(vSolutions[0])), subscript)) {
4664-
whichType = Solver(subscript, vSolutions);
4665-
if (whichType != TxoutType::WITNESS_V0_KEYHASH || !provider->GetPubKey(CKeyID(uint160(vSolutions[0])), pubkey))
4661+
uint160 hash;
4662+
if (whichType == TxoutType::WITNESS_V0_SCRIPTHASH) {
4663+
CRIPEMD160 hasher;
4664+
hasher.Write(&vSolutions[0][0], 32).Finalize(hash.begin());
4665+
} else // whichType == TxoutType::SCRIPTHASH
4666+
hash = uint160(vSolutions[0]);
4667+
if (provider && provider->GetCScript(CScriptID(hash), subscript)) {
4668+
TxoutType scriptType = Solver(subscript, vSolutions);
4669+
if (fProofOfStake && (scriptType == TxoutType::MULTISIG || scriptType == TxoutType::MULTISIG_DATA))
4670+
whichType = scriptType; // pubkey is retrieved from PoS output below
4671+
else if (scriptType != TxoutType::WITNESS_V0_KEYHASH || !provider->GetPubKey(CKeyID(uint160(vSolutions[0])), pubkey))
46664672
return false;
46674673
} else
46684674
return false;
@@ -4676,6 +4682,34 @@ bool CWallet::SignBlock(CBlock& block) const
46764682
return false;
46774683
}
46784684

4685+
if (fProofOfStake) {
4686+
TxoutType outputType = Solver(txout.scriptPubKey, vSolutions); // check the output
4687+
if (outputType == TxoutType::PUBKEY) { // pubkey is revealed in p2pk output
4688+
pubkey = CPubKey(vSolutions[0]); // use pubkey from output rather than input
4689+
pubkeyInSig = true;
4690+
} else if (whichType == TxoutType::PUBKEY || whichType == TxoutType::MULTISIG || whichType == TxoutType::MULTISIG_DATA) { // p2pk and multisig PoS inputs don't place the pubkey in the scriptSig
4691+
pubkeyInSig = false;
4692+
if (outputType == TxoutType::PUBKEYHASH || outputType == TxoutType::WITNESS_V0_KEYHASH) {
4693+
std::unique_ptr<SigningProvider> provider = GetSolvingProvider(txout.scriptPubKey);
4694+
if (!provider || !provider->GetPubKey(CKeyID(uint160(vSolutions[0])), pubkey)) // extract pubkey from output to put in coinbase
4695+
return false;
4696+
} else
4697+
return false;
4698+
} else
4699+
pubkeyInSig = true;
4700+
} else if (whichType != TxoutType::PUBKEY) // PoW has no inputs so the pubkey isn't revealed unless it uses a p2pk output
4701+
pubkeyInSig = false;
4702+
else
4703+
pubkeyInSig = true;
4704+
4705+
return true;
4706+
}
4707+
4708+
// peercoin: sign block
4709+
bool CWallet::SignBlock(CBlock& block, const CPubKey& pubkey) const
4710+
{
4711+
AssertLockHeld(cs_wallet);
4712+
46794713
// Try to sign with all ScriptPubKeyMans
46804714
for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
46814715
if (spk_man->SignBlock(block, pubkey) == SigningResult::OK)

src/wallet/wallet.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -984,7 +984,8 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
984984
*/
985985
void CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm);
986986
bool SelectStakeCoins(std::set<CInputCoin>& setCoins) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
987-
bool SignBlock(CBlock& block) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
987+
bool GetBlockSigningPubKey(const CBlock& block, CPubKey& pubkey, bool& pubkeyInSig) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
988+
bool SignBlock(CBlock& block, const CPubKey& pubkey) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
988989

989990
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
990991
{

0 commit comments

Comments
 (0)