Skip to content

Commit

Permalink
Set stake_modifieri + functional tests
Browse files Browse the repository at this point in the history
Signed-off-by: Julian Fleischer <[email protected]>
  • Loading branch information
scravy committed Apr 12, 2019
1 parent 5ba666c commit 13234ec
Show file tree
Hide file tree
Showing 29 changed files with 238 additions and 120 deletions.
2 changes: 1 addition & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,6 @@ libunite_server_a_SOURCES = \
staking/coin.cpp \
staking/legacy_validation_interface.cpp \
staking/network.cpp \
staking/proof_of_stake.cpp \
staking/stake_validator.cpp \
staking/staking_rpc.cpp \
staking/validation_error.cpp \
Expand Down Expand Up @@ -513,6 +512,7 @@ libunite_consensus_a_SOURCES = \
script/script_error.cpp \
script/script_error.h \
serialize.h \
staking/proof_of_stake.cpp \
tinyformat.h \
uint256.cpp \
uint256.h \
Expand Down
6 changes: 6 additions & 0 deletions src/primitives/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ class CBlock : public CBlockHeader
//! \brief (Re)computes the merkle trees of this block.
void ComputeMerkleTrees();

const CTxIn &GetStakingInput() const {
assert(!vtx.empty() && "GetStakingInput() invoked on an empty block");
assert(vtx[0]->vin.size() >= 2 && "GetStakingInput() invoked in a block that has no staking input");
return vtx[0]->vin[1];
}

std::string ToString() const;
};

Expand Down
2 changes: 1 addition & 1 deletion src/proposer/proposer_rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class ProposerRPCImpl : public ProposerRPC {
return proposerstatus(request);
}

UniValue getstakeablecoins(const JSONRPCRequest &request) const override {
UniValue liststakeablecoins(const JSONRPCRequest &request) const override {
CWallet *const wallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(wallet, request.fHelp)) {
return NullUniValue;
Expand Down
2 changes: 1 addition & 1 deletion src/proposer/proposer_rpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class ProposerRPC {

virtual UniValue proposerwake(const JSONRPCRequest &request) const = 0;

virtual UniValue getstakeablecoins(const JSONRPCRequest &request) const = 0;
virtual UniValue liststakeablecoins(const JSONRPCRequest &request) const = 0;

virtual ~ProposerRPC() = default;

Expand Down
6 changes: 3 additions & 3 deletions src/proposer/proposer_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ struct State {

Status m_status = Status::NOT_PROPOSING;

//! \brief how often the proposer looked for an eligible coin
//! \brief how many times the proposer looked for an eligible coin
std::uint64_t m_number_of_searches = 0;

//! \brief how often the proposer found an eligible coin
//! \brief how many times the proposer found an eligible coin
std::uint64_t m_number_of_search_attempts = 0;

//! \brief how often the proposer actually proposed
//! \brief how many blocks the proposer actually proposed
std::uint64_t m_number_of_proposed_blocks = 0;

//! \brief how many transactions the proposer included in proposed blocks in total
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/proposing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
t.appendCommand(NAME.name, &NAME);

void RegisterProposerRPCCommands(CRPCTable &t) {
PROPOSER_RPC_COMMAND(getstakeablecoins);
PROPOSER_RPC_COMMAND(liststakeablecoins);
PROPOSER_RPC_COMMAND(proposerstatus);
PROPOSER_RPC_COMMAND(proposerwake);
}
1 change: 1 addition & 0 deletions src/rpc/staking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
t.appendCommand(NAME.name, &NAME);

void RegisterStakingRPCCommands(CRPCTable &t) {
STAKING_RPC_COMMAND(calcstakemodifier, "txid", "prev");
STAKING_RPC_COMMAND(tracechain, "start", "length");
STAKING_RPC_COMMAND(tracestake, "start", "length", "reverse");
}
4 changes: 2 additions & 2 deletions src/snapshot/rpc_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,8 @@ UniValue calcsnapshothash(const JSONRPCRequest &request) {
stream >> inputs;
stream >> outputs;

uint256 stake_modifier = uint256(ParseHex(request.params[2].get_str()));
uint256 chain_work = uint256(ParseHex(request.params[3].get_str()));
const uint256 stake_modifier = uint256(ParseHex(request.params[2].get_str()));
const uint256 chain_work = uint256(ParseHex(request.params[3].get_str()));

SnapshotHash hash;
if (request.params.size() == 5) {
Expand Down
41 changes: 25 additions & 16 deletions src/staking/block_validator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,20 @@ class BlockValidatorImpl : public AbstractBlockValidator {
//! - the first input contains only meta information
//! - the first input's scriptSig contains the block height and snapshot hash
void CheckCoinbaseTransactionInternal(
const CTransactionRef &tx, //!< [in] The transaction to check
blockchain::Height &height_out, //!< [out] The height extracted from the scriptSig
uint256 &snapshot_hash_out, //!< [out] The snapshot hash extracted from the scriptSig
const CTransaction &tx, //!< [in] The transaction to check
blockchain::Height *height_out, //!< [out] The height extracted from the scriptSig
uint256 *snapshot_hash_out, //!< [out] The snapshot hash extracted from the scriptSig
BlockValidationResult &result //!< [in,out] The validation result
) const {
if (tx->vin.empty()) {
if (tx.vin.empty()) {
result.errors += Error::NO_META_INPUT;
} else {
CheckCoinbaseMetaInputInternal(tx->vin[0], height_out, snapshot_hash_out, result);
CheckCoinbaseMetaInputInternal(tx.vin[0], height_out, snapshot_hash_out, result);
}
if (tx->vin.size() < 2) {
if (tx.vin.size() < 2) {
result.AddError(Error::NO_STAKING_INPUT);
}
if (tx->vout.empty()) {
if (tx.vout.empty()) {
result.AddError(Error::COINBASE_TRANSACTION_WITHOUT_OUTPUT);
}
}
Expand All @@ -69,8 +69,8 @@ class BlockValidatorImpl : public AbstractBlockValidator {
//! It is then either terminated by OP_0 or some data follows (forwards-compatible).
void CheckCoinbaseMetaInputInternal(
const CTxIn &in, //!< [in] The input to check
blockchain::Height &height_out, //!< [out] The height extracted from the scriptSig
uint256 &snapshot_hash_out, //!< [out] The snapshot hash extracted from the scriptSig
blockchain::Height *height_out, //!< [out] The height extracted from the scriptSig
uint256 *snapshot_hash_out, //!< [out] The snapshot hash extracted from the scriptSig
BlockValidationResult &result //!< [in,out] The validation result
) const {

Expand All @@ -92,8 +92,8 @@ class BlockValidatorImpl : public AbstractBlockValidator {
CScriptNum height(buf, true);
if (height < 0 || height > std::numeric_limits<blockchain::Height>::max()) {
result.AddError(Error::INVALID_BLOCK_HEIGHT);
} else {
height_out = static_cast<blockchain::Height>(height.getint());
} else if (height_out) {
*height_out = static_cast<blockchain::Height>(height.getint());
}
} catch (scriptnum_error &) {
result.AddError(Error::INVALID_BLOCK_HEIGHT);
Expand All @@ -105,7 +105,9 @@ class BlockValidatorImpl : public AbstractBlockValidator {
result.AddError(Error::NO_SNAPSHOT_HASH);
return;
}
snapshot_hash_out = uint256(buf);
if (snapshot_hash_out) {
*snapshot_hash_out = uint256(buf);
}
}

//! \brief Checks the blocks signature.
Expand Down Expand Up @@ -164,8 +166,8 @@ class BlockValidatorImpl : public AbstractBlockValidator {

void CheckBlockInternal(
const CBlock &block, //!< [in] The block to check
blockchain::Height &height_out, //!< [out] The height extracted from the scriptSig
uint256 &snapshot_hash_out, //!< [out] The snapshot hash extracted from the scriptSig
blockchain::Height *height_out, //!< [out] The height extracted from the scriptSig
uint256 *snapshot_hash_out, //!< [out] The snapshot hash extracted from the scriptSig
BlockValidationResult &result //!< [in,out] The validation result
) const override {

Expand All @@ -177,7 +179,7 @@ class BlockValidatorImpl : public AbstractBlockValidator {

// check that coinbase transaction is first transaction
if (block.vtx[0]->GetType() == +TxType::COINBASE) {
CheckCoinbaseTransactionInternal(block.vtx[0], height_out, snapshot_hash_out, result);
CheckCoinbaseTransactionInternal(*block.vtx[0], height_out, snapshot_hash_out, result);
} else {
result.AddError(Error::FIRST_TRANSACTION_NOT_A_COINBASE_TRANSACTION);
}
Expand Down Expand Up @@ -241,6 +243,13 @@ class BlockValidatorImpl : public AbstractBlockValidator {
}

public:
BlockValidationResult CheckCoinbaseTransaction(
const CTransaction &coinbase_tx) const override {
BlockValidationResult result;
CheckCoinbaseTransactionInternal(coinbase_tx, nullptr, nullptr, result);
return result;
}

explicit BlockValidatorImpl(Dependency<blockchain::Behavior> blockchain_behavior)
: m_blockchain_behavior(blockchain_behavior) {}
};
Expand Down Expand Up @@ -269,7 +278,7 @@ BlockValidationResult AbstractBlockValidator::CheckBlock(
// perform the actual checks
blockchain::Height height;
uint256 snapshot_hash;
CheckBlockInternal(block, height, snapshot_hash, result);
CheckBlockInternal(block, &height, &snapshot_hash, result);
// save results in block_validation_info if present
if (block_validation_info) {
if (result) {
Expand Down
13 changes: 11 additions & 2 deletions src/staking/block_validator.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,15 @@ class BlockValidator {
BlockValidationInfo *info //!< [in,out] Access to the validation info for this block (optional, nullptr may be passed).
) const = 0;

//! \brief checks the coinbase transaction to be well-formed
//!
//! A coinbase transaction is expected to have at least two inputs:
//! - the meta input carrying height and snapshot hash at vin[0]
//! - the staking input at vin[1]
virtual BlockValidationResult CheckCoinbaseTransaction(
const CTransaction &coinbase_tx //!< [in] The coinbase transaction to check
) const = 0;

virtual ~BlockValidator() = default;

static std::unique_ptr<BlockValidator> New(Dependency<blockchain::Behavior>);
Expand All @@ -129,8 +138,8 @@ class AbstractBlockValidator : public BlockValidator {

virtual void CheckBlockInternal(
const CBlock &block,
blockchain::Height &height_out,
uint256 &snapshot_hash_out,
blockchain::Height *height_out,
uint256 *snapshot_hash_out,
BlockValidationResult &result) const = 0;

virtual void ContextualCheckBlockInternal(
Expand Down
8 changes: 4 additions & 4 deletions src/staking/legacy_validation_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ class LegacyValidationImpl : public LegacyValidationInterface {
if (check_merkle_root) {
bool mutated;
uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated);
if (block.hashMerkleRoot != hashMerkleRoot2)
if (block.hashMerkleRoot != hashMerkleRoot2) {
return state.DoS(100, false, REJECT_INVALID, "bad-txnmrklroot", true, "hashMerkleRoot mismatch");

}
// Check for merkle tree malleability (CVE-2012-2459): repeating sequences
// of transactions in a block without affecting the merkle root of a block,
// while still invalidating it.
if (mutated)
if (mutated) {
return state.DoS(100, false, REJECT_INVALID, "bad-txns-duplicate", true, "duplicate transaction");

}
uint256 merkle_root = BlockFinalizerCommitsMerkleRoot(block);
if (block.hash_finalizer_commits_merkle_root != merkle_root) {
return state.DoS(100, false, REJECT_INVALID, "bad-finalizercommits-merkleroot", true, "hash_finalizer_commits_merkle_root mismatch");
Expand Down
29 changes: 29 additions & 0 deletions src/staking/proof_of_stake.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <script/script.h>
#include <script/standard.h>
#include <streams.h>

namespace staking {

Expand Down Expand Up @@ -100,4 +101,32 @@ std::vector<CPubKey> ExtractBlockSigningKeys(const CBlock &block) {
return ExtractBlockSigningKeys(coinbase_inputs[1]);
}

uint256 ComputeKernelHash(const uint256 &previous_block_stake_modifier,
const blockchain::Time stake_block_time,
const uint256 &stake_txid,
const std::uint32_t stake_out_index,
const blockchain::Time target_block_time) {

CDataStream s(SER_GETHASH, 0);

s << previous_block_stake_modifier;
s << stake_block_time;
s << stake_txid;
s << stake_out_index;
s << target_block_time;

return Hash(s.begin(), s.end());
}

uint256 ComputeStakeModifier(const uint256 &stake_transaction_hash,
const uint256 &previous_blocks_stake_modifier) {

CDataStream s(SER_GETHASH, 0);

s << stake_transaction_hash;
s << previous_blocks_stake_modifier;

return Hash(s.begin(), s.end());
}

} // namespace staking
27 changes: 27 additions & 0 deletions src/staking/proof_of_stake.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#ifndef UNIT_E_STAKING_PROOF_OF_STAKE_H
#define UNIT_E_STAKING_PROOF_OF_STAKE_H

#include <blockchain/blockchain_types.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <pubkey.h>
Expand Down Expand Up @@ -58,6 +59,32 @@ std::vector<CPubKey> ExtractBlockSigningKeys(const CTxIn &);
//! and forwards the call to ExtractBlockSigningKeys(const TxIn &).
std::vector<CPubKey> ExtractBlockSigningKeys(const CBlock &);

//! \brief Computes the kernel hash which determines whether you're eligible for proposing or not.
//!
//! The kernel hash must not rely on the contents of the block as this would allow a proposer
//! to degrade the system into a PoW setting simply by selecting subsets of transactions to
//! include (this also allows a proposer to produce multiple eligible blocks with different
//! contents which is why detection of duplicate stake is crucial).
//!
//! At the same time the kernel hash must not be easily predictable, which is why some entropy
//! is added: The "stake modifier" is a value taken from a previous block.
//!
//! In case one is not eligible to propose: The cards are being reshuffled every so often,
//! which is why the "current time" (the block time of the block to propose) is part of the
//! computation for the kernel hash.
uint256 ComputeKernelHash(const uint256 &previous_block_stake_modifier,
blockchain::Time stake_block_time,
const uint256 &stake_txid,
std::uint32_t stake_out_index,
blockchain::Time target_block_time);

//! \brief Computes the stake modifier which is used to make the next kernel unpredictable.
//!
//! The stake modifier relies on the transaction hash of the coin staked and
//! the stake modifier of the previous block.
uint256 ComputeStakeModifier(const uint256 &stake_transaction_hash,
const uint256 &previous_blocks_stake_modifier);

} // namespace staking

#endif //UNIT_E_STAKING_PROOF_OF_STAKE_H
Loading

0 comments on commit 13234ec

Please sign in to comment.