Skip to content

Commit

Permalink
Merge pull request #2 from cakedefi/feature_PoS_kernel
Browse files Browse the repository at this point in the history
Feature PoS kernel
  • Loading branch information
uzyn authored Sep 13, 2019
2 parents 54467fb + 19e52cc commit 66026be
Show file tree
Hide file tree
Showing 16 changed files with 415 additions and 3 deletions.
4 changes: 4 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ BITCOIN_CORE_H = \
policy/policy.h \
policy/rbf.h \
policy/settings.h \
pos.h \
pos_kernel.h \
pow.h \
protocol.h \
psbt.h \
Expand Down Expand Up @@ -286,6 +288,8 @@ libbitcoin_server_a_SOURCES = \
policy/fees.cpp \
policy/rbf.cpp \
policy/settings.cpp \
pos.cpp \
pos_kernel.cpp \
pow.cpp \
rest.cpp \
rpc/blockchain.cpp \
Expand Down
28 changes: 27 additions & 1 deletion src/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <uint256.h>

#include <vector>
#include <boost/optional.hpp>

/**
* Maximum amount of time that a block timestamp is allowed to exceed the
Expand Down Expand Up @@ -181,6 +182,10 @@ class CBlockIndex
uint32_t nTime;
uint32_t nBits;
uint32_t nNonce;
boost::optional<CBlockHeader::PoS> proofOfStakeBody;

// proof-of-stake specific fields
uint256 stakeModifier; // hash modifier for proof-of-stake

//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
int32_t nSequenceId;
Expand All @@ -204,6 +209,10 @@ class CBlockIndex
nSequenceId = 0;
nTimeMax = 0;

// PoS
proofOfStakeBody = boost::optional<CBlockHeader::PoS>{};
stakeModifier = uint256{};

nVersion = 0;
hashMerkleRoot = uint256();
nTime = 0;
Expand All @@ -225,6 +234,8 @@ class CBlockIndex
nTime = block.nTime;
nBits = block.nBits;
nNonce = block.nNonce;
stakeModifier = block.stakeModifier;
proofOfStakeBody = block.proofOfStakeBody;
}

FlatFilePos GetBlockPos() const {
Expand Down Expand Up @@ -255,6 +266,8 @@ class CBlockIndex
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
block.stakeModifier = stakeModifier;
block.proofOfStakeBody = proofOfStakeBody;
return block;
}

Expand Down Expand Up @@ -300,8 +313,9 @@ class CBlockIndex

std::string ToString() const
{
return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)",
return strprintf("CBlockIndex(pprev=%p, nHeight=%d, stakeModifier=(%s), merkle=%s, hashBlock=%s)",
pprev, nHeight,
stakeModifier.ToString(),
hashMerkleRoot.ToString(),
GetBlockHash().ToString());
}
Expand Down Expand Up @@ -329,6 +343,11 @@ class CBlockIndex
return false;
}

bool IsProofOfStake() const
{
return (bool) proofOfStakeBody;
}

//! Build the skiplist pointer for this entry.
void BuildSkip();

Expand Down Expand Up @@ -376,6 +395,11 @@ class CDiskBlockIndex : public CBlockIndex
if (nStatus & BLOCK_HAVE_UNDO)
READWRITE(VARINT(nUndoPos));

//PoS serialization
CBlockHeader::PoS loc_proofOfStake = proofOfStakeBody ? *proofOfStakeBody : CBlockHeader::PoS{};
READWRITE(loc_proofOfStake);
proofOfStakeBody = loc_proofOfStake;

// block header
READWRITE(this->nVersion);
READWRITE(hashPrev);
Expand All @@ -394,6 +418,8 @@ class CDiskBlockIndex : public CBlockIndex
block.nTime = nTime;
block.nBits = nBits;
block.nNonce = nNonce;
block.stakeModifier = stakeModifier;
block.proofOfStakeBody = proofOfStakeBody;
return block.GetHash();
}

Expand Down
32 changes: 32 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ class CMainParams : public CChainParams {
consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8");
consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
consensus.BIP66Height = 363725; // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931

consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.pos.nTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.pos.nTargetSpacing = 10 * 60; // 10 minutes
consensus.pos.fAllowMinDifficultyBlocks = false; // only for regtest
consensus.pos.fNoRetargeting = false; // only for regtest

consensus.pos.coinstakeMaturity = 100;

consensus.pos.allowMintingWithoutPeers = false; // don't mint if no peers connected

consensus.CSVHeight = 419328; // 000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5
consensus.SegwitHeight = 481824; // 0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893
consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
Expand Down Expand Up @@ -175,6 +186,17 @@ class CTestNetParams : public CChainParams {
consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8");
consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
consensus.BIP66Height = 330776; // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182

consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.pos.nTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.pos.nTargetSpacing = 10 * 60; // 10 minutes
consensus.pos.fAllowMinDifficultyBlocks = false; // only for regtest
consensus.pos.fNoRetargeting = false; // only for regtest

consensus.pos.coinstakeMaturity = 100;

consensus.pos.allowMintingWithoutPeers = false; // don't mint if no peers connected

consensus.CSVHeight = 770112; // 00000000025e930139bac5c6c31a403776da130831ab85be56578f3fa75369bb
consensus.SegwitHeight = 834624; // 00000000002b980fcd729daaa248fd9316a5200e9b367f4ff2c42453e84201ca
consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
Expand Down Expand Up @@ -259,6 +281,16 @@ class CRegTestParams : public CChainParams {
consensus.BIP34Hash = uint256();
consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in functional tests)
consensus.BIP66Height = 1251; // BIP66 activated on regtest (Used in functional tests)
consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.pos.nTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.pos.nTargetSpacing = 10 * 60; // 10 minutes
consensus.pos.fAllowMinDifficultyBlocks = false; // only for regtest
consensus.pos.fNoRetargeting = false; // only for regtest

consensus.pos.coinstakeMaturity = 100;

consensus.pos.allowMintingWithoutPeers = false; // don't mint if no peers connected

consensus.CSVHeight = 432; // CSV activated on regtest (Used in rpc activation tests)
consensus.SegwitHeight = 0; // SEGWIT is always activated on regtest unless overridden
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
Expand Down
17 changes: 17 additions & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <limits>
#include <map>
#include <string>
#include <arith_uint256.h>

namespace Consensus {

Expand Down Expand Up @@ -62,6 +63,22 @@ struct Params {
* Note that segwit v0 script rules are enforced on all blocks except the
* BIP 16 exception blocks. */
int SegwitHeight;

struct PoS {
uint256 diffLimit;
int64_t nTargetTimespan;
int64_t nTargetSpacing;
bool fAllowMinDifficultyBlocks;
bool fNoRetargeting;

int64_t DifficultyAdjustmentInterval() const { return nTargetTimespan / nTargetSpacing; }

arith_uint256 interestAtoms = arith_uint256{10000000000000000ull};
bool allowMintingWithoutPeers;
int coinstakeMaturity = 500;
};
PoS pos;

/**
* Minimum blocks including miner confirmation of the total of 2016 blocks in a retargeting period,
* (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments.
Expand Down
3 changes: 3 additions & 0 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <policy/feerate.h>
#include <policy/policy.h>
#include <pow.h>
#include <pos_kernel.h>
#include <primitives/transaction.h>
#include <script/standard.h>
#include <timedata.h>
Expand Down Expand Up @@ -162,6 +163,8 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
pblock->nNonce = 0;
pblock->stakeModifier = uint256{}; // SS

pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);

CValidationState state;
Expand Down
128 changes: 128 additions & 0 deletions src/pos.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#include <pos.h>
#include <pos_kernel.h>
#include <pow.h>
#include <chain.h>
#include <validation.h>

#include <wallet/wallet.h>
#include <txdb.h>

static bool CheckStakeModifier(const CBlockIndex* pindexPrev, const CBlock& block, const Consensus::Params& params) {
if (block.hashPrevBlock.IsNull())
return block.stakeModifier.IsNull();

return block.stakeModifier == pos::ComputeStakeModifier_PoS(pindexPrev->stakeModifier,
block.proofOfStakeBody->coinstakePrevout);
}

bool CheckBlockProof_headerOnly(const CBlockHeader& block, const Consensus::Params& params) {
return pos::CheckProofOfStake_headerOnly(block, params);
}

bool CheckBlockProof(const CBlockIndex* pindexPrev, const CBlock& block, CCoinsViewCache& view,
const Consensus::Params& params) {
if (!CheckStakeModifier(pindexPrev, block, params)) {
return false;
}

return pos::CheckProofOfStake(pindexPrev, block, view, params);

}

namespace pos {

/// Check PoS signatures (PoS block hashes are signed with coinstake out pubkey)
bool CheckHeaderSignature(const CBlockHeader& block) {
if (!block.IsProofOfStake()) {
return true;
}

if (block.proofOfStakeBody->sig.empty()) {
LogPrintf("CheckBlockSignature: Bad Block - PoS signature is empty\n");
return false;
}

CPubKey recoveredPubKey{};
if (!recoveredPubKey.RecoverCompact(block.GetHashToSign(), block.proofOfStakeBody->sig)) {
LogPrintf("CheckBlockSignature: Bad Block - malformed signature\n");
return false;
}

if (recoveredPubKey.GetID() != block.proofOfStakeBody->pubKeyHash) {
LogPrintf("CheckBlockSignature: Bad Block - wrong signature\n");
return false;
}
return true;
}

bool CheckProofOfStake_headerOnly(const CBlockHeader& block, const Consensus::Params& params) {
if (!block.IsProofOfStake()) {
return error("CheckProofOfStake_headerOnly(): called on non-PoS %s", block.GetHash().ToString());
}

const int64_t coinstakeTime = (int64_t) block.GetBlockTime();
const auto& body = *block.proofOfStakeBody; // checked to exist after IsProofOfStake

// checking PoS kernel is faster, so check it first
if (!CheckKernelHash(block.stakeModifier, block.nBits, coinstakeTime, body.coinstakeAmount,
body.coinstakePrevout, params).hashOk) {
return false;
}
return CheckHeaderSignature(block);
}

bool CheckProofOfStake(const CBlockIndex* pindexPrev, const CBlock& block, CCoinsViewCache& view,
const Consensus::Params& params) {
if (block.IsProofOfStake() != block.HasCoinstakeTx()) {
return false; // block claimed it's PoS, but doesn't have coinstakeTx
}
if (!block.IsCompleteProofOfStake()) {
return error("CheckProofOfStake(): called on a non-PoS block %s", block.GetHash().ToString());
}

const auto& body = *block.proofOfStakeBody; // checked to exist after IsProofOfStake

if (body.coinstakePrevout != block.vtx[1]->vin[0].prevout)
return error("CheckProofOfStake(): block claimed PoS prevout doesn't match coinstakeTx (%s != %s)", body.coinstakePrevout.ToString(), block.vtx[1]->vin[0].prevout.ToString());

// check staker's pubKeyHash
{
std::vector<CTxDestination> addressRet;
txnouttype typeRet;
int nRet;
if (!ExtractDestinations(block.vtx[1]->vout[1].scriptPubKey, typeRet, addressRet, nRet))
return error("CheckProofOfStake(): coinstakeTx scriptPubKey must be P2PKH");
if (!(typeRet == txnouttype::TX_PUBKEYHASH && addressRet.size() == 1))
return error("CheckProofOfStake(): coinstakeTx scriptPubKey must be P2PKH");

CKeyID keyID(boost::get<PKHash>(addressRet[0]));
if (keyID != body.pubKeyHash)
return error("CheckProofOfStake(): coinstakeTx scriptPubKey and block pubKeyHash mismatch");
}

// check staker's coin
{
const Coin& stakeCoin = view.AccessCoin(body.coinstakePrevout);
if (stakeCoin.IsSpent())
return error("CheckProofOfStake : Could not find previous transaction for PoS %s\n",
body.coinstakePrevout.hash.ToString());
if ((pindexPrev->nHeight - stakeCoin.nHeight) < params.pos.coinstakeMaturity)
return error("CheckProofOfStake(): coinstakeTx input must have at least 100 confirmations");

if (body.coinstakeAmount != stakeCoin.out.nValue)
return error("CheckProofOfStake(): coinstakeTx amount and block coinstakeAmount mismatch");
// it's vital for security that we use the same scriptPubKey
if (block.vtx[1]->vout[1].scriptPubKey != stakeCoin.out.scriptPubKey)
return error("CheckProofOfStake(): coinstakeTx scriptPubKey and prev. scriptPubKey mismatch");
}

const int64_t coinstakeTime = (int64_t) block.GetBlockTime();

// checking PoS kernel is faster, so check it first
if (!CheckKernelHash(block.stakeModifier, block.nBits, coinstakeTime, body.coinstakeAmount, body.coinstakePrevout,
params).hashOk) {
return false;
}
return CheckHeaderSignature(block);
}
}
27 changes: 27 additions & 0 deletions src/pos.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <consensus/params.h>
#include <arith_uint256.h>

class CBlock;

class CBlockIndex;

class CBlockHeader;

class CCoinsViewCache;

namespace pos {

/// Check PoS signatures (PoS block hashes are signed with privkey of first coinstake out pubkey)
bool CheckHeaderSignature(const CBlockHeader& block);

/// Check kernel hash target and coinstake signature
bool CheckProofOfStake_headerOnly(const CBlockHeader& block, const Consensus::Params& params);

/// Check kernel hash target and coinstake signature. Check that block coinstakeTx matches header
bool CheckProofOfStake(const CBlockIndex* pindexPrev, const CBlock& block, CCoinsViewCache& view,
const Consensus::Params& params);

}

Loading

0 comments on commit 66026be

Please sign in to comment.