-\feffusing System;
+\feff/**
+* Novacoin classes library
+* Copyright (C) 2015 Alex D. (balthazar.ad@gmail.com)
+
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as
+* published by the Free Software Foundation, either version 3 of the
+* License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+using System;
using System.IO;
using System.Linq;
using System.Collections.Concurrent;
public long nStakeModifier { get; set; }
/// <summary>
+ /// Stake modifier checksum.
+ /// </summary>
+ public uint nStakeModifierChecksum { get; set; }
+
+ /// <summary>
/// Chain trust score
/// </summary>
public byte[] ChainTrust { get; set; }
/// </summary>
private ConcurrentDictionary<uint256, CTransactionStoreItem> txMap = new ConcurrentDictionary<uint256, CTransactionStoreItem>();
+ private ConcurrentDictionary<uint256, uint256> mapProofOfStake = new ConcurrentDictionary<uint256, uint256>();
+
public static CBlockStore Instance;
/// <summary>
return false; // SetStakeEntropyBit() failed
}
- // TODO: set stake entropy bit, record proof-of-stake hash value
+ // Save proof-of-stake hash value
+ if (itemTemplate.IsProofOfStake)
+ {
+ uint256 hashProofOfStake;
+ if (!CBlockStore.Instance.GetProofOfStakeHash(blockHash, out hashProofOfStake))
+ {
+ return false; // hashProofOfStake not found
+ }
+ itemTemplate.hashProofOfStake = hashProofOfStake;
+ }
// TODO: compute stake modifier
+ // ppcoin: compute stake modifier
+ long nStakeModifier = 0;
+ bool fGeneratedStakeModifier = false;
+ if (!StakeModifier.ComputeNextStakeModifier(itemTemplate, ref nStakeModifier, ref fGeneratedStakeModifier))
+ {
+ return false; // ComputeNextStakeModifier() failed
+ }
+
+ itemTemplate.SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
+ itemTemplate.nStakeModifierChecksum = StakeModifier.GetStakeModifierChecksum(itemTemplate);
+
+ // TODO: verify stake modifier checkpoints
+
// Add to index
if (block.IsProofOfStake)
{
return blockMap.TryAdd(blockHash, itemTemplate);
}
+ /// <summary>
+ /// Try to find proof-of-stake hash in the map.
+ /// </summary>
+ /// <param name="blockHash">Block hash</param>
+ /// <param name="hashProofOfStake">Proof-of-stake hash</param>
+ /// <returns>Proof-of-Stake hash value</returns>
+ private bool GetProofOfStakeHash(uint256 blockHash, out uint256 hashProofOfStake)
+ {
+ return mapProofOfStake.TryGetValue(blockHash, out hashProofOfStake);
+ }
+
public bool AcceptBlock(ref CBlock block)
{
uint256 nHash = block.header.Hash;
return true;
}
- public bool GetBlock(uint256 blockHash, ref CBlock block)
+ public bool GetBlock(uint256 blockHash, ref CBlock block, ref long nBlockPos)
{
var reader = new BinaryReader(fStreamReadWrite).BaseStream;
if (QueryBlock.Count == 1)
{
+ nBlockPos = QueryBlock[0].nBlockPos;
return QueryBlock[0].ReadFromFile(ref reader, out block);
}
return false;
}
+ public bool GetByTransactionID(uint256 TxID, ref CBlock block, ref CTransaction tx, ref long nBlockPos, ref long nTxPos)
+ {
+ var QueryTx = dbConn.Query<CTransactionStoreItem>("select * from [TransactionStorage] where [TransactionHash] = ?", (byte[])TxID);
+
+ if (QueryTx.Count == 1)
+ {
+ nTxPos = QueryTx[0].nTxPos;
+ return GetBlock(QueryTx[0].BlockHash, ref block, ref nBlockPos);
+ }
+
+ // Tx not found
+
+ return false;
+ }
+
/// <summary>
/// Get block cursor from map.
/// </summary>
}
// TODO: proof-of-stake validation
+
+ uint256 hashProofOfStake = 0, targetProofOfStake = 0;
+ if (!StakeModifier.CheckProofOfStake(block.vtx[1], block.header.nBits, ref hashProofOfStake, ref targetProofOfStake))
+ {
+ return false; // do not error here as we expect this during initial block download
+ }
+ if (!mapProofOfStake.ContainsKey(blockHash))
+ {
+ // add to mapProofOfStake
+ mapProofOfStake.TryAdd(blockHash, hashProofOfStake);
+ }
+
}
// TODO: difficulty verification
-\feffusing System;
+\feff/**
+* Novacoin classes library
+* Copyright (C) 2015 Alex D. (balthazar.ad@gmail.com)
+
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as
+* published by the Free Software Foundation, either version 3 of the
+* License, or (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
namespace Novacoin
{
+ /// <summary>
+ /// Stake modifier calculation. Doesn't work properly, for now.
+ /// </summary>
public class StakeModifier
{
/// <summary>
/// Get stake modifier selection interval (in seconds)
/// </summary>
/// <returns></returns>
- static long GetStakeModifierSelectionInterval()
+ internal static long GetStakeModifierSelectionInterval()
{
long nSelectionInterval = 0;
for (int nSection = 0; nSection < 64; nSection++)
/// <param name="nStakeModifierPrev">Previous value of stake modifier.</param>
/// <param name="selectedCursor">Selection result.</param>
/// <returns></returns>
- static bool SelectBlockFromCandidates(List<Tuple<uint, uint256>> sortedByTimestamp, Dictionary<uint256, CBlockStoreItem> mapSelectedBlocks, long nSelectionIntervalStop, long nStakeModifierPrev, ref CBlockStoreItem selectedCursor)
+ internal static bool SelectBlockFromCandidates(List<Tuple<uint, uint256>> sortedByTimestamp, Dictionary<uint256, CBlockStoreItem> mapSelectedBlocks, long nSelectionIntervalStop, long nStakeModifierPrev, ref CBlockStoreItem selectedCursor)
{
bool fSelected = false;
uint256 hashBest = 0;
/// additional bits in the stake modifier, even after generating a chain of
/// blocks.
/// </summary>
- bool ComputeNextStakeModifier(CBlockStoreItem cursorCurrent, ref long nStakeModifier, ref bool fGeneratedStakeModifier)
+ public static bool ComputeNextStakeModifier(CBlockStoreItem cursorCurrent, ref long nStakeModifier, ref bool fGeneratedStakeModifier)
{
nStakeModifier = 0;
fGeneratedStakeModifier = false;
return true;
}
- bool GetKernelStakeModifier(uint256 hashBlockFrom, ref long nStakeModifier)
+ public static bool GetKernelStakeModifier(uint256 hashBlockFrom, ref long nStakeModifier)
{
uint nStakeModifierHeight = 0;
uint nStakeModifierTime = 0;
return GetKernelStakeModifier(hashBlockFrom, ref nStakeModifier, ref nStakeModifierHeight, ref nStakeModifierTime);
}
- bool CheckStakeKernelHash(uint nBits, CBlock blockFrom, uint nTxPrevOffset, CTransaction txPrev, COutPoint prevout, uint nTimeTx, ref uint256 hashProofOfStake, ref uint256 targetProofOfStake)
+ public static bool CheckStakeKernelHash(uint nBits, uint256 hashBlockFrom, uint nTimeBlockFrom, uint nTxPrevOffset, CTransaction txPrev, COutPoint prevout, uint nTimeTx, ref uint256 hashProofOfStake, ref uint256 targetProofOfStake)
{
if (nTimeTx < txPrev.nTime)
{
return false; // Transaction timestamp violation
}
- uint nTimeBlockFrom = blockFrom.header.nTime;
if (nTimeBlockFrom + nStakeMinAge > nTimeTx) // Min age requirement
{
return false; // Min age violation
nTargetPerCoinDay.Compact = nBits;
ulong nValueIn = txPrev.vout[prevout.n].nValue;
- uint256 hashBlockFrom = blockFrom.header.Hash;
uint256 nCoinDayWeight = new uint256(nValueIn) * GetWeight(txPrev.nTime, nTimeTx) / CTransaction.nCoin / (24 * 60 * 60);
targetProofOfStake = nCoinDayWeight * nTargetPerCoinDay;
return Math.Min(nIntervalEnd - nIntervalBeginning - nStakeMinAge, nStakeMaxAge);
}
+
+ internal static uint GetStakeModifierChecksum(CBlockStoreItem itemTemplate)
+ {
+ Contract.Assert(itemTemplate.prev != null || (uint256)itemTemplate.Hash == NetUtils.nHashGenesisBlock);
+
+ // Hash previous checksum with flags, hashProofOfStake and nStakeModifier
+ MemoryStream ss = new MemoryStream();
+ BinaryWriter writer = new BinaryWriter(ss);
+
+ if (itemTemplate.prev != null)
+ {
+ writer.Write(itemTemplate.prev.nStakeModifierChecksum);
+ }
+
+ writer.Write((uint)itemTemplate.BlockTypeFlag);
+
+ if (itemTemplate.IsProofOfStake)
+ {
+ writer.Write(itemTemplate.hashProofOfStake);
+ }
+ else
+ {
+ writer.Write(new uint256(0));
+ }
+ writer.Write(itemTemplate.nStakeModifier);
+
+ uint256 hashChecksum = CryptoUtils.ComputeHash256(ss.ToArray());
+ writer.Close();
+
+ hashChecksum >>= (256 - 32);
+
+ return (uint)hashChecksum.Low64;
+ }
+
+ internal static bool CheckProofOfStake(CTransaction tx, uint nBits, ref uint256 hashProofOfStake, ref uint256 targetProofOfStake)
+ {
+ if (!tx.IsCoinStake)
+ {
+ return false; // called on non-coinstake
+ }
+
+ // Kernel (input 0) must match the stake hash target per coin age (nBits)
+ CTxIn txin = tx.vin[0];
+
+ // Read block header
+
+ CBlock block = null;
+ CTransaction txPrev = null;
+ long nBlockPos = 0, nTxPos = 0;
+
+ if (!CBlockStore.Instance.GetByTransactionID(txin.prevout.hash, ref block, ref txPrev, ref nBlockPos, ref nTxPos))
+ {
+ return false; // unable to read block of previous transaction
+ }
+
+ if (!CheckStakeKernelHash(nBits, block.header.Hash, block.header.nTime, (uint)(nTxPos - nBlockPos), txPrev, txin.prevout, tx.nTime, ref hashProofOfStake, ref targetProofOfStake))
+ {
+ return false; // check kernel failed on coinstake
+ }
+
+ return true;
+ }
}
}