X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=Novacoin%2FStakeModifier.cs;h=882a699486564ed6c1f6c5992322bfe37fc8bc45;hb=04ac3ad40812b5ff686f16e182a10a78b9816bf4;hp=2448497b4c0c21af4fbeb2bc22001bab92978362;hpb=12f67828e449407a93bdb3202066003a48a8c276;p=NovacoinLibrary.git diff --git a/Novacoin/StakeModifier.cs b/Novacoin/StakeModifier.cs index 2448497..882a699 100644 --- a/Novacoin/StakeModifier.cs +++ b/Novacoin/StakeModifier.cs @@ -1,14 +1,33 @@ -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; using System.Linq; -using System.Numerics; -using System.Text; -using System.Threading.Tasks; namespace Novacoin { + /// + /// Stake modifier calculation. Doesn't work properly, for now. + /// public class StakeModifier { /// @@ -98,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++) @@ -118,14 +137,14 @@ 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; selectedCursor = null; foreach (var item in sortedByTimestamp) { - CBlockStoreItem cursor = CBlockStore.Instance.GetCursor(item.Item2); + CBlockStoreItem cursor = CBlockStore.Instance.GetMapCursor(item.Item2); if (cursor == null) { @@ -147,7 +166,7 @@ namespace Novacoin // compute the selection hash by hashing its proof-hash and the // previous proof-of-stake modifier - uint256 hashProof = cursor.IsProofOfStake ? (uint256)cursor.hashProofOfStake : selectedBlockHash; + var hashProof = cursor.IsProofOfStake ? (uint256)cursor.hashProofOfStake : selectedBlockHash; uint256 hashSelection; var s = new MemoryStream(); @@ -194,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; @@ -261,7 +280,7 @@ namespace Novacoin } // write the entropy bit of the selected block - nStakeModifierNew |= ((cursor.StakeEntropyBit) << nRound); + nStakeModifierNew |= (((long)cursor.StakeEntropyBit) << nRound); // add the selected block from candidates to selected list mapSelectedBlocks.Add(cursor.Hash, cursor); @@ -276,10 +295,13 @@ namespace Novacoin /// The stake modifier used to hash for a stake kernel is chosen as the stake /// modifier about a selection interval later than the coin generating the kernel /// - static bool GetKernelStakeModifier(uint256 hashBlockFrom, ref long nStakeModifier, ref uint nStakeModifierHeight, ref uint nStakeModifierTime) + static bool GetKernelStakeModifier(uint256 hashBlockFrom, out long nStakeModifier, out uint nStakeModifierHeight, out uint nStakeModifierTime) { nStakeModifier = 0; - var cursorFrom = CBlockStore.Instance.GetCursor(hashBlockFrom); + nStakeModifierTime = 0; + nStakeModifierHeight = 0; + + var cursorFrom = CBlockStore.Instance.GetMapCursor(hashBlockFrom); if (cursorFrom == null) { return false; // Block not indexed @@ -311,41 +333,41 @@ namespace Novacoin return true; } - bool GetKernelStakeModifier(uint256 hashBlockFrom, ref long nStakeModifier) + public static bool GetKernelStakeModifier(uint256 hashBlockFrom, out long nStakeModifier) { uint nStakeModifierHeight = 0; uint nStakeModifierTime = 0; - return GetKernelStakeModifier(hashBlockFrom, ref nStakeModifier, ref nStakeModifierHeight, ref nStakeModifierTime); + return GetKernelStakeModifier(hashBlockFrom, out nStakeModifier, out nStakeModifierHeight, out 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, out uint256 hashProofOfStake, out uint256 targetProofOfStake) { + hashProofOfStake = targetProofOfStake = 0; + 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 } - uint256 nTargetPerCoinDay = new uint256(); + uint256 nTargetPerCoinDay = 0; nTargetPerCoinDay.Compact = nBits; ulong nValueIn = txPrev.vout[prevout.n].nValue; - uint256 hashBlockFrom = blockFrom.header.Hash; - BigInteger bnCoinDayWeight = (long)nValueIn * GetWeight(txPrev.nTime, nTimeTx) / (long)CTransaction.nCoin / (24 * 60 * 60); + uint256 nCoinDayWeight = new uint256(nValueIn) * GetWeight(txPrev.nTime, nTimeTx) / CTransaction.nCoin / (24 * 60 * 60); - targetProofOfStake = (bnCoinDayWeight * new BigInteger(nTargetPerCoinDay)).ToByteArray(); + targetProofOfStake = nCoinDayWeight * nTargetPerCoinDay; // Calculate hash - long nStakeModifier = 0; - uint nStakeModifierHeight = 0; - uint nStakeModifierTime = 0; - if (!GetKernelStakeModifier(hashBlockFrom, ref nStakeModifier, ref nStakeModifierHeight, ref nStakeModifierTime)) + long nStakeModifier; + uint nStakeModifierTime; + uint nStakeModifierHeight; + if (!GetKernelStakeModifier(hashBlockFrom, out nStakeModifier, out nStakeModifierHeight, out nStakeModifierTime)) { return false; } @@ -371,7 +393,7 @@ namespace Novacoin } // Get time weight using supplied timestamps - static long GetWeight(long nIntervalBeginning, long nIntervalEnd) + static ulong GetWeight(ulong nIntervalBeginning, ulong nIntervalEnd) { // Kernel hash weight starts from 0 at the 30-day min age // this change increases active coins participating the hash and helps @@ -381,5 +403,92 @@ namespace Novacoin return Math.Min(nIntervalEnd - nIntervalBeginning - nStakeMinAge, nStakeMaxAge); } + + internal static uint GetStakeModifierChecksum(ref CBlockStoreItem itemTemplate) + { + Contract.Assert(itemTemplate.prev != null || (uint256)itemTemplate.Hash == NetInfo.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; + } + + public static bool CheckProofOfStake(CTransaction tx, uint nBits, out uint256 hashProofOfStake, out uint256 targetProofOfStake) + { + hashProofOfStake = targetProofOfStake = 0; + + 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 + + long nBlockPos; + CBlock block; + if (!CBlockStore.Instance.GetBlockByTransactionID(txin.prevout.hash, out block, out nBlockPos)) + { + return false; // unable to read block of previous transaction + } + + long nTxPos = 0; + CTransaction txPrev = null; + + // Iterate through vtx array + for (var i = 0; i < block.vtx.Length; i++) + { + if (block.vtx[i].Hash == txin.prevout.hash) + { + txPrev = block.vtx[i]; + nTxPos = nBlockPos + block.GetTxOffset(i); + + break; + } + } + + if (txPrev == null) + { + return false; // No such transaction found in the block + } + + if (!ScriptCode.VerifyScript(txin.scriptSig, txPrev.vout[txin.prevout.n].scriptPubKey, tx, 0, (int)scriptflag.SCRIPT_VERIFY_P2SH, 0)) + { + return false; // vin[0] signature check failed + } + + if (!CheckStakeKernelHash(nBits, block.header.Hash, block.header.nTime, (uint)(nTxPos - nBlockPos), txPrev, txin.prevout, tx.nTime, out hashProofOfStake, out targetProofOfStake)) + { + return false; // check kernel failed on coinstake + } + + return true; + } } }