Skip to content

Commit

Permalink
Block signature for shield staking blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
panleone committed Jul 16, 2023
1 parent 00a3795 commit 8c86cca
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 15 deletions.
19 changes: 10 additions & 9 deletions src/blockassembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ static CMutableTransaction NewCoinbase(const int nHeight, const CScript* pScript
return txCoinbase;
}

bool SolveProofOfStake(CBlock* pblock, CBlockIndex* pindexPrev, CWallet* pwallet, const std::vector<std::unique_ptr<CStakeableInterface>>& availableCoins, bool stopPoSOnNewBlock)
bool SolveProofOfStake(CBlock* pblock, CBlockIndex* pindexPrev, CMutableTransaction& txCoinStake, CWallet* pwallet, const std::vector<std::unique_ptr<CStakeableInterface>>& availableCoins, bool stopPoSOnNewBlock)
{
boost::this_thread::interruption_point();

Expand All @@ -79,14 +79,13 @@ bool SolveProofOfStake(CBlock* pblock, CBlockIndex* pindexPrev, CWallet* pwallet
// Sync wallet before create coinstake
pwallet->BlockUntilSyncedToCurrentChain();

CMutableTransaction txCoinStake;
int64_t nTxNewTime = 0;
CStakeableInterface* pStake = pwallet->CreateCoinStake(*pindexPrev,
pblock->nBits,
txCoinStake,
nTxNewTime,
availableCoins,
stopPoSOnNewBlock);
pblock->nBits,
txCoinStake,
nTxNewTime,
availableCoins,
stopPoSOnNewBlock);
if (!pStake) {
LogPrint(BCLog::STAKING, "%s : stake not found\n", __func__);
return false;
Expand Down Expand Up @@ -193,8 +192,10 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblock->nVersion = gArgs.GetArg("-blockversion", pblock->nVersion);
}

CMutableTransaction txCoinStake;

// Depending on the tip height, try to find a coinstake who solves the block or create a coinbase tx.
if (!(fProofOfStake ? SolveProofOfStake(pblock, pindexPrev, pwallet, availableCoins, stopPoSOnNewBlock) : CreateCoinbaseTx(pblock, scriptPubKeyIn, pindexPrev))) {
if (!(fProofOfStake ? SolveProofOfStake(pblock, pindexPrev, txCoinStake, pwallet, availableCoins, stopPoSOnNewBlock) : CreateCoinbaseTx(pblock, scriptPubKeyIn, pindexPrev))) {
return nullptr;
}

Expand Down Expand Up @@ -244,7 +245,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
if (fProofOfStake) { // this is only for PoS because the IncrementExtraNonce does it for PoW
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
LogPrintf("CPUMiner : proof-of-stake block found %s \n", pblock->GetHash().GetHex());
if (!SignBlock(*pblock, *pwallet)) {
if (!SignBlock(*pblock, *pwallet, txCoinStake.shieldStakeRandomness, txCoinStake.shieldStakePrivKey)) {
LogPrintf("%s: Signing new block with UTXO key failed \n", __func__);
return nullptr;
}
Expand Down
23 changes: 22 additions & 1 deletion src/blocksignature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "blocksignature.h"

#include "rust/include/librustzcash.h"
#include "script/standard.h"
#include "zpiv/zpivmodule.h"

Expand Down Expand Up @@ -32,9 +33,19 @@ bool SignBlockWithKey(CBlock& block, const CKey& key)
return true;
}

bool SignBlock(CBlock& block, const CKeyStore& keystore)
bool SignBlock(CBlock& block, const CKeyStore& keystore, Optional<uint256> shieldStakeRandomness, Optional<uint256> shieldStakePrivKey)
{
CKeyID keyID;

if (block.IsProofOfShieldStake()) {
block.vchBlockSig.resize(64);
if (shieldStakeRandomness == boost::none || shieldStakePrivKey == boost::none) {
return error("%s: failed to find keys for shield stake", __func__);
}
if (!librustzcash_sign_block((*shieldStakePrivKey).begin(), (*shieldStakeRandomness).begin(), block.GetHash().begin(), block.vchBlockSig.data())) error("%s: failed to sign the shield stake block", __func__);
return true;
}

if (!GetKeyIDFromUTXO(block.vtx[1]->vout[1], keyID)) {
return error("%s: failed to find key for PoS", __func__);
}
Expand All @@ -50,7 +61,17 @@ bool CheckBlockSignature(const CBlock& block)
{
if (block.IsProofOfWork())
return block.vchBlockSig.empty();
if (block.IsProofOfShieldStake()) {
uint256 rk = block.vtx[1]->sapData->vShieldedSpend[0].rk;

if (block.vchBlockSig.size() != 64) {
return error("%s: vchBlockSig has not the right length!", __func__);
}
// Sorry for this but everything involving directly vchBlockSig inside the librustzcash_verify_block_signature call does not work for some reason
unsigned char sigArr[64];
std::copy(block.vchBlockSig.begin(), block.vchBlockSig.end(), sigArr);
return librustzcash_verify_block_signature(rk.begin(), block.GetHash().begin(), sigArr);
}
if (block.vchBlockSig.empty())
return error("%s: vchBlockSig is empty!", __func__);

Expand Down
6 changes: 4 additions & 2 deletions src/blocksignature.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
#define PIVX_BLOCKSIGNATURE_H

#include "key.h"
#include "primitives/block.h"
#include "keystore.h"
#include "optional.h"
#include "primitives/block.h"
#include "sapling/transaction_builder.h"

bool SignBlockWithKey(CBlock& block, const CKey& key);
bool SignBlock(CBlock& block, const CKeyStore& keystore);
bool SignBlock(CBlock& block, const CKeyStore& keystore, Optional<uint256> shieldStakeRandomness = boost::none, Optional<uint256> shieldStakePrivKey = boost::none);
bool CheckBlockSignature(const CBlock& block);

#endif //PIVX_BLOCKSIGNATURE_H
2 changes: 1 addition & 1 deletion src/primitives/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class CBlock : public CBlockHeader
{
READWRITEAS(CBlockHeader, obj);
READWRITE(obj.vtx);
if(obj.vtx.size() > 1 && obj.vtx[1]->IsCoinStake())
if (obj.vtx.size() > 1 && (obj.vtx[1]->IsCoinStake() || obj.vtx[1]->IsCoinShieldStake()))
READWRITE(obj.vchBlockSig);

// Shield Staking Proof
Expand Down
4 changes: 4 additions & 0 deletions src/primitives/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,10 @@ struct CMutableTransaction
Optional<SaplingTxData> sapData{SaplingTxData()}; // Future: Don't initialize it by default
Optional<std::vector<uint8_t>> extraPayload{nullopt};

// It's very convenient having shield stake signing keys here
Optional<uint256> shieldStakeRandomness = boost::none;
Optional<uint256> shieldStakePrivKey = boost::none;

CMutableTransaction();
CMutableTransaction(const CTransaction& tx);

Expand Down
13 changes: 13 additions & 0 deletions src/rust/include/librustzcash.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ extern "C" {
unsigned char *result
);

/// Verify the block signature for shield staking
bool librustzcash_verify_block_signature(
const unsigned char* rk,
const unsigned char* sighash,
const unsigned char* sign);

/// Computes the signature for a shield staking block
bool librustzcash_sign_block(
const unsigned char* ask,
const unsigned char* ar,
const unsigned char* sighash,
unsigned char* result);

/// Computes the signature for each Spend description, given the key
/// `ask`, the re-randomization `ar`, the 32-byte sighash `sighash`,
/// and an output `result` buffer of 64-bytes for the signature.
Expand Down
53 changes: 53 additions & 0 deletions src/rust/src/rustzcash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,59 @@ pub extern "system" fn librustzcash_sapling_output_proof(
true
}

#[no_mangle]
pub extern "system" fn librustzcash_verify_block_signature(
rk: *const [c_uchar; 32],
sighash: *const [c_uchar; 64],
sign: *const [c_uchar; 64],
) -> bool {
// Deserialize rk
let rk = match redjubjub::PublicKey::<Bls12>::read(&(unsafe { &*rk })[..], &JUBJUB) {
Ok(p) => p,
Err(_) => return false,
};

// Deserialize the signature
let signature = match Signature::read(&(unsafe { &*sign })[..]) {
Ok(sig) => sig,
Err(_) => return false,
};
rk.verify(unsafe { &*sighash}, &signature, FixedGenerators::SpendingKeyGenerator, &JUBJUB)
}

#[no_mangle]
pub extern "system" fn librustzcash_sign_block(
ask: *const [c_uchar; 32],
ar: *const [c_uchar; 32],
sighash: *const [c_uchar; 64],
result: *mut [c_uchar; 64],
) -> bool {
// The caller provides the re-randomization of `ak`.
let ar = match Fs::from_repr(read_fs(&(unsafe { &*ar })[..])) {
Ok(p) => p,
Err(_) => return false,
};

// The caller provides `ask`, the spend authorizing key.
let ask = match redjubjub::PrivateKey::<Bls12>::read(&(unsafe { &*ask })[..]) {
Ok(p) => p,
Err(_) => return false,
};

// Initialize secure RNG
let mut rng = OsRng;

// Do the signing
let rsk = ask.randomize(ar);
let sig = rsk.sign(unsafe { &*sighash }, &mut rng, FixedGenerators::SpendingKeyGenerator, &JUBJUB);

// Write out the signature
sig.write(&mut (unsafe { &mut *result })[..])
.expect("result should be 64 bytes");

return true
}

#[no_mangle]
pub extern "system" fn librustzcash_sapling_spend_sig(
ask: *const [c_uchar; 32],
Expand Down
5 changes: 5 additions & 0 deletions src/sapling/transaction_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ void TransactionBuilder::SetFee(CAmount _fee)
this->fee = _fee;
}

uint256 TransactionBuilder::GetShieldStakeRandomness()
{
return spends[0].alpha;
}

void TransactionBuilder::SendChangeTo(const libzcash::SaplingPaymentAddress& changeAddr, const uint256& ovk)
{
saplingChangeAddr = std::make_pair(ovk, changeAddr);
Expand Down
3 changes: 3 additions & 0 deletions src/sapling/transaction_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ class TransactionBuilder

void SetFee(CAmount _fee);

// returns the randomness required to compute the blocksignature in shield stake
uint256 GetShieldStakeRandomness();

void AddStakeInput();
// Throws if the anchor does not match the anchor used by
// previously-added Sapling spends.
Expand Down
5 changes: 3 additions & 2 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3430,7 +3430,7 @@ CStakeableInterface* CWallet::CreateCoinStake(const CBlockIndex& indexPrev, unsi

// Add block reward to the credit
if (stakeOutput->CreateReward(*this, indexPrev, txNew)) {
return stakeOutput.get();
return stakeOutput.get();
}
}

Expand All @@ -3440,7 +3440,6 @@ CStakeableInterface* CWallet::CreateCoinStake(const CBlockIndex& indexPrev, unsi
bool CWallet::CreateShieldReward(const CBlockIndex& indexPrev, const CStakeableShieldNote& shieldNote, CMutableTransaction& txNew)
{
CAmount nMasternodePayment = GetMasternodePayment(indexPrev.nHeight + 1);

TransactionBuilder txBuilder(Params().GetConsensus(), this);
txBuilder.SetFee(0);
txBuilder.AddStakeInput();
Expand All @@ -3459,6 +3458,8 @@ bool CWallet::CreateShieldReward(const CBlockIndex& indexPrev, const CStakeableS
const auto& txTrial = txBuilder.Build().GetTx();
if (txTrial) {
txNew = CMutableTransaction(*txTrial);
txNew.shieldStakePrivKey = sk.expsk.ask;
txNew.shieldStakeRandomness = txBuilder.GetShieldStakeRandomness();
return true;
} else {
return false;
Expand Down
1 change: 1 addition & 0 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "primitives/transaction.h"
#include "sapling/address.h"
#include "sapling/saplingscriptpubkeyman.h"
#include "sapling/transaction_builder.h"
#include "script/ismine.h"
#include "stakeinput.h"
#include "util/system.h"
Expand Down

0 comments on commit 8c86cca

Please sign in to comment.