From: CryptoManiac Date: Thu, 3 Sep 2015 22:06:46 +0000 (+0300) Subject: Initial implementation of stake modifier calculation. X-Git-Url: https://git.novaco.in/?p=NovacoinLibrary.git;a=commitdiff_plain;h=217d4232e9feeb787b9163b2ca1e701840d5b65b Initial implementation of stake modifier calculation. Note that this functionality doesn't work properly yet. --- diff --git a/Novacoin/CBlockStore.cs b/Novacoin/CBlockStore.cs index 94fc2b6..935a4ab 100644 --- a/Novacoin/CBlockStore.cs +++ b/Novacoin/CBlockStore.cs @@ -1,4 +1,23 @@ -using System; +/** +* 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 . +*/ + + +using System; using System.IO; using System.Linq; using System.Collections.Concurrent; @@ -76,6 +95,11 @@ namespace Novacoin public long nStakeModifier { get; set; } /// + /// Stake modifier checksum. + /// + public uint nStakeModifierChecksum { get; set; } + + /// /// Chain trust score /// public byte[] ChainTrust { get; set; } @@ -560,6 +584,8 @@ namespace Novacoin /// private ConcurrentDictionary txMap = new ConcurrentDictionary(); + private ConcurrentDictionary mapProofOfStake = new ConcurrentDictionary(); + public static CBlockStore Instance; /// @@ -676,10 +702,32 @@ namespace Novacoin 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) { @@ -730,6 +778,17 @@ namespace Novacoin return blockMap.TryAdd(blockHash, itemTemplate); } + /// + /// Try to find proof-of-stake hash in the map. + /// + /// Block hash + /// Proof-of-stake hash + /// Proof-of-Stake hash value + 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; @@ -786,7 +845,7 @@ namespace Novacoin 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; @@ -794,6 +853,7 @@ namespace Novacoin if (QueryBlock.Count == 1) { + nBlockPos = QueryBlock[0].nBlockPos; return QueryBlock[0].ReadFromFile(ref reader, out block); } @@ -802,6 +862,21 @@ namespace Novacoin return false; } + public bool GetByTransactionID(uint256 TxID, ref CBlock block, ref CTransaction tx, ref long nBlockPos, ref long nTxPos) + { + var QueryTx = dbConn.Query("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; + } + /// /// Get block cursor from map. /// @@ -867,6 +942,18 @@ namespace Novacoin } // 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 diff --git a/Novacoin/NetInfo.cs b/Novacoin/NetInfo.cs index d8ac0cf..1cf6f54 100644 --- a/Novacoin/NetInfo.cs +++ b/Novacoin/NetInfo.cs @@ -14,6 +14,8 @@ namespace Novacoin public static uint nChainChecksSwitchTime = 1379635200; // Fri, 20 Sep 2013 00:00:00 GMT + public static uint256 nHashGenesisBlock = new uint256("00000a060336cbb72fe969666d337b87198b1add2abaa59cca226820b32933a4"); + public static readonly uint nLockTimeThreshold = 500000000; private static readonly uint nDrift = 7200; diff --git a/Novacoin/StakeModifier.cs b/Novacoin/StakeModifier.cs index e19a8f0..7f45eb8 100644 --- a/Novacoin/StakeModifier.cs +++ b/Novacoin/StakeModifier.cs @@ -1,4 +1,23 @@ -using System; +/** +* 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 . +*/ + + +using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.IO; @@ -6,6 +25,9 @@ using System.Linq; namespace Novacoin { + /// + /// Stake modifier calculation. Doesn't work properly, for now. + /// public class StakeModifier { /// @@ -95,7 +117,7 @@ namespace Novacoin /// Get stake modifier selection interval (in seconds) /// /// - static long GetStakeModifierSelectionInterval() + internal static long GetStakeModifierSelectionInterval() { long nSelectionInterval = 0; for (int nSection = 0; nSection < 64; nSection++) @@ -115,7 +137,7 @@ namespace Novacoin /// Previous value of stake modifier. /// Selection result. /// - static bool SelectBlockFromCandidates(List> sortedByTimestamp, Dictionary mapSelectedBlocks, long nSelectionIntervalStop, long nStakeModifierPrev, ref CBlockStoreItem selectedCursor) + internal static bool SelectBlockFromCandidates(List> sortedByTimestamp, Dictionary mapSelectedBlocks, long nSelectionIntervalStop, long nStakeModifierPrev, ref CBlockStoreItem selectedCursor) { bool fSelected = false; uint256 hashBest = 0; @@ -191,7 +213,7 @@ namespace Novacoin /// additional bits in the stake modifier, even after generating a chain of /// blocks. /// - 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; @@ -308,7 +330,7 @@ namespace Novacoin return true; } - bool GetKernelStakeModifier(uint256 hashBlockFrom, ref long nStakeModifier) + public static bool GetKernelStakeModifier(uint256 hashBlockFrom, ref long nStakeModifier) { uint nStakeModifierHeight = 0; uint nStakeModifierTime = 0; @@ -316,14 +338,13 @@ namespace Novacoin 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 @@ -333,7 +354,6 @@ namespace Novacoin 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; @@ -378,5 +398,67 @@ namespace Novacoin 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; + } } }