X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=Novacoin%2FStakeModifier.cs;h=030c3ce518df3381309c183a5b782fe47037a213;hb=47b7e832b9a92860ab2129f5bfa6b17dd049bdf0;hp=f8aad9de7e3280b3b475b293a33bd40ab36fe434;hpb=ebe1463806632670bdef2b5af8a405ab061cf058;p=NovacoinLibrary.git
diff --git a/Novacoin/StakeModifier.cs b/Novacoin/StakeModifier.cs
index f8aad9d..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,7 +295,7 @@ 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, out long nStakeModifier, out uint nStakeModifierHeight, out uint nStakeModifierTime)
+ private static bool GetKernelStakeModifier(ref uint256 hashBlockFrom, out long nStakeModifier, out uint nStakeModifierHeight, out uint nStakeModifierTime)
{
nStakeModifier = 0;
nStakeModifierTime = 0;
@@ -333,12 +333,12 @@ namespace Novacoin
return true;
}
- public static bool GetKernelStakeModifier(uint256 hashBlockFrom, out long nStakeModifier)
+ private static bool GetKernelStakeModifier(ref uint256 hashBlockFrom, out long nStakeModifier)
{
uint nStakeModifierHeight = 0;
uint nStakeModifierTime = 0;
- return GetKernelStakeModifier(hashBlockFrom, out nStakeModifier, out nStakeModifierHeight, out 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, out uint256 hashProofOfStake, out uint256 targetProofOfStake)
@@ -358,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;
- uint nStakeModifierTime;
- uint nStakeModifierHeight;
- if (!GetKernelStakeModifier(hashBlockFrom, out nStakeModifier, out nStakeModifierHeight, out nStakeModifierTime))
+ 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
@@ -404,37 +410,37 @@ 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);
-
- // Hash previous checksum with flags, hashProofOfStake and nStakeModifier
- MemoryStream ss = new MemoryStream();
- BinaryWriter writer = new BinaryWriter(ss);
+ Contract.Assert(cursorBlock.prev != null || (uint256)cursorBlock.Hash == NetInfo.nHashGenesisBlock);
- if (itemTemplate.prev != null)
- {
- writer.Write(itemTemplate.prev.nStakeModifierChecksum);
- }
+ var stream = new MemoryStream();
+ var bw = new BinaryWriter(stream);
- 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;
}
public static bool CheckProofOfStake(CTransaction tx, uint nBits, out uint256 hashProofOfStake, out uint256 targetProofOfStake)
@@ -451,8 +457,8 @@ namespace Novacoin
// Read block header
- long 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
@@ -462,22 +468,16 @@ 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);
-
- break;
- }
- }
+ var nTxPrevIndex = Array.FindIndex(block.vtx, txItem => txItem.Hash == txin.prevout.hash);
- 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