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;
+ }
}
}