X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=Novacoin%2FStakeModifier.cs;h=030c3ce518df3381309c183a5b782fe47037a213;hb=47b7e832b9a92860ab2129f5bfa6b17dd049bdf0;hp=944fd36c15c0756c4ef28ecdf8b2a19c361a1d05;hpb=f02df773a836f8b3df12bd21fd418f7c17b77821;p=NovacoinLibrary.git diff --git a/Novacoin/StakeModifier.cs b/Novacoin/StakeModifier.cs index 944fd36..030c3ce 100644 --- a/Novacoin/StakeModifier.cs +++ b/Novacoin/StakeModifier.cs @@ -28,7 +28,7 @@ namespace Novacoin /// /// Stake modifier calculation. Doesn't work properly, for now. /// - public class StakeModifier + public static class StakeModifier { /// /// 30 days as zero time weight @@ -60,14 +60,14 @@ namespace Novacoin /// /// Mon, 20 Oct 2014 00:00:00 GMT /// - internal const uint nModifierSwitchTime = 1413763200; + public const uint nModifierSwitchTime = 1413763200; /// /// Whether the given block is subject to new modifier protocol /// /// Block timestamp /// Result - internal static bool IsFixedModifierInterval(uint nTimeBlock) + private static bool IsFixedModifierInterval(uint nTimeBlock) { return (nTimeBlock >= nModifierSwitchTime); } @@ -79,7 +79,7 @@ namespace Novacoin /// Stake modifier (ref) /// Stake modifier generation time (ref) /// - internal static bool GetLastStakeModifier(CBlockStoreItem cursor, ref long nStakeModifier, ref uint nModifierTime) + private static bool GetLastStakeModifier(CBlockStoreItem cursor, ref long nStakeModifier, ref uint nModifierTime) { if (cursor == null) { @@ -107,7 +107,7 @@ namespace Novacoin /// /// /// - internal static long GetStakeModifierSelectionIntervalSection(int nSection) + private static long GetStakeModifierSelectionIntervalSection(int nSection) { Contract.Assert(nSection >= 0 && nSection < 64); return (nModifierInterval * 63 / (63 + ((63 - nSection) * (nModifierIntervalRatio - 1)))); @@ -117,7 +117,7 @@ namespace Novacoin /// Get stake modifier selection interval (in seconds) /// /// - internal static long GetStakeModifierSelectionInterval() + private static long GetStakeModifierSelectionInterval() { long nSelectionInterval = 0; for (int nSection = 0; nSection < 64; nSection++) @@ -137,7 +137,7 @@ namespace Novacoin /// Previous value of stake modifier. /// Selection result. /// - internal static bool SelectBlockFromCandidates(List> sortedByTimestamp, Dictionary mapSelectedBlocks, long nSelectionIntervalStop, long nStakeModifierPrev, ref CBlockStoreItem selectedCursor) + private static bool SelectBlockFromCandidates(List> sortedByTimestamp, Dictionary mapSelectedBlocks, long nSelectionIntervalStop, long nStakeModifierPrev, ref CBlockStoreItem selectedCursor) { bool fSelected = false; uint256 hashBest = 0; @@ -169,13 +169,13 @@ namespace Novacoin var hashProof = cursor.IsProofOfStake ? (uint256)cursor.hashProofOfStake : selectedBlockHash; uint256 hashSelection; - var s = new MemoryStream(); - var writer = new BinaryWriter(s); + var stream = new MemoryStream(); + var bw = new BinaryWriter(stream); - writer.Write(hashProof); - writer.Write(nStakeModifierPrev); - hashSelection = CryptoUtils.ComputeHash256(s.ToArray()); - writer.Close(); + bw.Write(hashProof); + bw.Write(nStakeModifierPrev); + hashSelection = CryptoUtils.ComputeHash256(stream.ToArray()); + bw.Close(); // the selection hash is divided by 2**32 so that proof-of-stake block // is always favored over proof-of-work block. this is to preserve @@ -295,9 +295,12 @@ 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) + private static bool GetKernelStakeModifier(ref uint256 hashBlockFrom, out long nStakeModifier, out uint nStakeModifierHeight, out uint nStakeModifierTime) { nStakeModifier = 0; + nStakeModifierTime = 0; + nStakeModifierHeight = 0; + var cursorFrom = CBlockStore.Instance.GetMapCursor(hashBlockFrom); if (cursorFrom == null) { @@ -330,16 +333,18 @@ namespace Novacoin return true; } - public static bool GetKernelStakeModifier(uint256 hashBlockFrom, ref long nStakeModifier) + private static bool GetKernelStakeModifier(ref uint256 hashBlockFrom, out long nStakeModifier) { uint nStakeModifierHeight = 0; uint nStakeModifierTime = 0; - return GetKernelStakeModifier(hashBlockFrom, ref nStakeModifier, ref nStakeModifierHeight, ref nStakeModifierTime); + return GetKernelStakeModifier(ref hashBlockFrom, out nStakeModifier, out nStakeModifierHeight, out nStakeModifierTime); } - public static bool CheckStakeKernelHash(uint nBits, uint256 hashBlockFrom, uint nTimeBlockFrom, 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 @@ -353,38 +358,44 @@ namespace Novacoin uint256 nTargetPerCoinDay = 0; nTargetPerCoinDay.Compact = nBits; - ulong nValueIn = txPrev.vout[prevout.n].nValue; - uint256 nCoinDayWeight = new uint256(nValueIn) * GetWeight(txPrev.nTime, nTimeTx) / CTransaction.nCoin / (24 * 60 * 60); + long nValueIn = txPrev.vout[prevout.n].nValue; + uint256 nCoinDayWeight = new uint256((ulong)nValueIn) * GetWeight(txPrev.nTime, nTimeTx) / CTransaction.nCoin / (24 * 60 * 60); 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; + if (!GetKernelStakeModifier(ref hashBlockFrom, out nStakeModifier)) { return false; } - MemoryStream s = new MemoryStream(); - BinaryWriter w = new BinaryWriter(s); + var stream = new MemoryStream(); + var bw = new BinaryWriter(stream); - w.Write(nStakeModifier); - w.Write(nTimeBlockFrom); - w.Write(nTxPrevOffset); - w.Write(txPrev.nTime); - w.Write(prevout.n); - w.Write(nTimeTx); + // Coinstake kernel (input 0) must meet the formula + // + // hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDayWeight + // + // This ensures that the chance of getting a coinstake is proportional to the + // amount of coin age one owns. + // + // Note that "+" is not arithmetic operation here, this means concatenation of byte arrays. + // + // Check https://github.com/novacoin-project/novacoin/wiki/Kernel for additional information. - hashProofOfStake = CryptoUtils.ComputeHash256(s.ToArray()); - w.Close(); + bw.Write(nStakeModifier); + bw.Write(nTimeBlockFrom); + bw.Write(nTxPrevOffset); + bw.Write(txPrev.nTime); + bw.Write(prevout.n); + bw.Write(nTimeTx); - // Now check if proof-of-stake hash meets target protocol - if (hashProofOfStake > targetProofOfStake) - return false; + hashProofOfStake = CryptoUtils.ComputeHash256(stream.ToArray()); + bw.Close(); - return true; + // Now check if proof-of-stake hash meets target protocol + return targetProofOfStake >= hashProofOfStake; } // Get time weight using supplied timestamps @@ -399,41 +410,43 @@ namespace Novacoin return Math.Min(nIntervalEnd - nIntervalBeginning - nStakeMinAge, nStakeMaxAge); } - internal static uint GetStakeModifierChecksum(CBlockStoreItem itemTemplate) + /// + /// Calculate stake modifier checksum. + /// + /// Block cursor. + /// Checksum value. + public static uint GetModifierChecksum(CBlockStoreItem cursorBlock) { - Contract.Assert(itemTemplate.prev != null || (uint256)itemTemplate.Hash == NetInfo.nHashGenesisBlock); + Contract.Assert(cursorBlock.prev != null || (uint256)cursorBlock.Hash == NetInfo.nHashGenesisBlock); - // Hash previous checksum with flags, hashProofOfStake and nStakeModifier - MemoryStream ss = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(ss); + var stream = new MemoryStream(); + var bw = new BinaryWriter(stream); - if (itemTemplate.prev != null) - { - writer.Write(itemTemplate.prev.nStakeModifierChecksum); - } - - writer.Write((uint)itemTemplate.BlockTypeFlag); + // Kernel hash for proof-of-stake or zero bytes array for proof-of-work + byte[] proofBytes = cursorBlock.IsProofOfStake ? cursorBlock.hashProofOfStake : new byte[32]; - if (itemTemplate.IsProofOfStake) - { - writer.Write(itemTemplate.hashProofOfStake); - } - else + // Hash previous checksum with flags, hashProofOfStake and nStakeModifier + if (cursorBlock.prev != null) { - writer.Write(new uint256(0)); + bw.Write(cursorBlock.prev.nStakeModifierChecksum); } - writer.Write(itemTemplate.nStakeModifier); - uint256 hashChecksum = CryptoUtils.ComputeHash256(ss.ToArray()); - writer.Close(); + bw.Write((uint)cursorBlock.BlockTypeFlag); + bw.Write(proofBytes); + bw.Write(cursorBlock.nStakeModifier); + + uint256 hashChecksum = CryptoUtils.ComputeHash256(stream.ToArray()); + bw.Close(); hashChecksum >>= (256 - 32); - return (uint)hashChecksum.Low64; + return hashChecksum.Low32; } - internal static bool CheckProofOfStake(CTransaction tx, uint nBits, ref uint256 hashProofOfStake, ref uint256 targetProofOfStake) + 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 @@ -444,9 +457,9 @@ namespace Novacoin // Read block header - long nBlockPos = 0; - CBlock block = null; - if (!CBlockStore.Instance.GetBlockByTransactionID(txin.prevout.hash, ref block, ref nBlockPos)) + CBlock block; + long nBlockPos; + if (!CBlockStore.Instance.GetBlockByTransactionID(txin.prevout.hash, out block, out nBlockPos)) { return false; // unable to read block of previous transaction } @@ -455,28 +468,22 @@ namespace Novacoin 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); + var nTxPrevIndex = Array.FindIndex(block.vtx, txItem => txItem.Hash == txin.prevout.hash); - break; - } - } - - if (txPrev == null) + if (nTxPrevIndex == -1) { return false; // No such transaction found in the block } + txPrev = block.vtx[nTxPrevIndex]; + nTxPos = nBlockPos + block.GetTxOffset(nTxPrevIndex); + 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, ref hashProofOfStake, ref targetProofOfStake)) + 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 }