X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=Novacoin%2FCBlock.cs;h=450612aaca85c1ed9f6a3c6f940193ce6e3a0668;hb=f11201b1edaaabe33a256da25333f4f26f0cc136;hp=8ac3caded8933593814873fe2fb04ee1bccfcc4d;hpb=886861c2656fd556145c7d46a2002c169dacc2f6;p=NovacoinLibrary.git diff --git a/Novacoin/CBlock.cs b/Novacoin/CBlock.cs index 8ac3cad..450612a 100644 --- a/Novacoin/CBlock.cs +++ b/Novacoin/CBlock.cs @@ -52,6 +52,11 @@ namespace Novacoin /// public const uint nMaxBlockSize = 1000000; + /// + /// Sanity threshold for amount of sigops. + /// + public const uint nMaxSigOps = 20000; + /// /// Block header. /// @@ -123,7 +128,7 @@ namespace Novacoin public bool CheckBlock(bool fCheckPOW = true, bool fCheckMerkleRoot = true, bool fCheckSig = true) { - var uniqueTX = new List(); // tx hashes + var uniqueTX = new List(); // tx hashes uint nSigOps = 0; // total sigops // Basic sanity checkings @@ -194,13 +199,13 @@ namespace Novacoin } // Check timestamp - if (header.nTime > NetUtils.FutureDrift(NetUtils.GetAdjustedTime())) + if (header.nTime > NetInfo.FutureDrift(NetInfo.GetAdjustedTime())) { return false; } // Check coinbase timestamp - if (header.nTime < NetUtils.PastDrift(vtx[0].nTime)) + if (header.nTime < NetInfo.PastDrift(vtx[0].nTime)) { return false; } @@ -250,7 +255,7 @@ namespace Novacoin } // Reject block if validation would consume too much resources. - if (nSigOps > 50000) + if (nSigOps > nMaxSigOps) { return false; } @@ -264,9 +269,24 @@ namespace Novacoin return true; } - private bool CheckProofOfWork(ScryptHash256 hash, uint nBits) + private static bool CheckProofOfWork(uint256 hash, uint nBits) { - // TODO: stub! + uint256 nTarget = new uint256(); + nTarget.Compact = nBits; + + // Check range + if (nTarget > NetInfo.nProofOfWorkLimit) + { + // nBits below minimum work + return false; + } + + // Check proof of work matches claimed amount + if (hash > nTarget) + { + // hash doesn't match nBits + return false; + } return true; } @@ -361,18 +381,18 @@ namespace Novacoin /// /// Serialized size /// - public int Size + public uint Size { get { - int nSize = 80 + VarInt.GetEncodedSize(vtx.Length); // CBlockHeader + NumTx + uint nSize = 80 + VarInt.GetEncodedSize(vtx.Length); // CBlockHeader + NumTx foreach (var tx in vtx) { nSize += tx.Size; } - nSize += VarInt.GetEncodedSize(signature.Length) + signature.Length; + nSize += VarInt.GetEncodedSize(signature.Length) + (uint)signature.Length; return nSize; } @@ -383,11 +403,11 @@ namespace Novacoin /// /// Transaction index. /// Offset in bytes from the beginning of block header. - public int GetTxOffset(int nTx) + public uint GetTxOffset(int nTx) { Contract.Requires(nTx >= 0 && nTx < vtx.Length, "Transaction index you've specified is incorrect."); - int nOffset = 80 + VarInt.GetEncodedSize(vtx.Length); // CBlockHeader + NumTx + uint nOffset = 80 + VarInt.GetEncodedSize(vtx.Length); // CBlockHeader + NumTx for (int i = 0; i < nTx; i++) { @@ -400,7 +420,7 @@ namespace Novacoin /// /// Merkle root /// - public Hash256 hashMerkleRoot + public uint256 hashMerkleRoot { get { @@ -408,7 +428,7 @@ namespace Novacoin foreach (var tx in vtx) { - merkleTree.AddRange(Hash256.ComputeRaw256(tx)); + merkleTree.AddRange(CryptoUtils.ComputeHash256(tx)); } int levelOffset = 0; @@ -421,12 +441,12 @@ namespace Novacoin var left = merkleTree.GetRange((levelOffset + nLeft) * 32, 32).ToArray(); var right = merkleTree.GetRange((levelOffset + nRight) * 32, 32).ToArray(); - merkleTree.AddRange(Hash256.ComputeRaw256(ref left, ref right)); + merkleTree.AddRange(CryptoUtils.ComputeHash256(ref left, ref right)); } levelOffset += nLevelSize; } - return (merkleTree.Count == 0) ? new Hash256() : new Hash256(merkleTree.GetRange(merkleTree.Count-32, 32).ToArray()); + return (merkleTree.Count == 0) ? 0 : (uint256)merkleTree.GetRange(merkleTree.Count-32, 32).ToArray(); } } @@ -450,6 +470,130 @@ namespace Novacoin return sb.ToString(); } - } + + /// + /// Calculate proof-of-work reward. + /// + /// Packed difficulty representation. + /// Amount of fees. + /// Reward value. + public static ulong GetProofOfWorkReward(uint nBits, ulong nFees) + { + // NovaCoin: subsidy is cut in half every 64x multiply of PoW difficulty + // A reasonably continuous curve is used to avoid shock to market + // (nSubsidyLimit / nSubsidy) ** 6 == bnProofOfWorkLimit / bnTarget + // + // Human readable form: + // + // nSubsidy = 100 / (diff ^ 1/6) + // + // Please note that we're using bisection to find an approximate solutuion + + + uint256 nTarget = 0; + nTarget.Compact = nBits; + + BigNum bnTarget = nTarget; + BigNum bnTargetLimit = NetInfo.nProofOfWorkLimit; + + BigNum bnSubsidyLimit = NetInfo.nMaxMintProofOfWork; + BigNum bnLowerBound = CTransaction.nCent; + BigNum bnUpperBound = bnSubsidyLimit; + + while (bnLowerBound + CTransaction.nCent <= bnUpperBound) + { + BigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2; + if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnTarget) + bnUpperBound = bnMidValue; + else + bnLowerBound = bnMidValue; + } + + ulong nSubsidy = bnUpperBound; + nSubsidy = (nSubsidy / CTransaction.nCent) * CTransaction.nCent; + + return Math.Min(nSubsidy, NetInfo.nMaxMintProofOfWork) + nFees; + } + + public static ulong GetProofOfStakeReward(ulong nCoinAge, uint nBits, uint nTime) + { + ulong nRewardCoinYear, nSubsidy, nSubsidyLimit = 10 * CTransaction.nCoin; + + if (nTime > NetInfo.nDynamicStakeRewardTime) + { + // Stage 2 of emission process is PoS-based. It will be active on mainNet since 20 Jun 2013. + + BigNum bnRewardCoinYearLimit = NetInfo.nMaxMintProofOfStake; // Base stake mint rate, 100% year interest + + uint256 nTarget = 0; + nTarget.Compact = nBits; + + BigNum bnTarget = nTarget; + BigNum bnTargetLimit = NetInfo.GetProofOfStakeLimit(0, nTime); + + // NovaCoin: A reasonably continuous curve is used to avoid shock to market + + BigNum bnLowerBound = CTransaction.nCent, // Lower interest bound is 1% per year + bnUpperBound = bnRewardCoinYearLimit, // Upper interest bound is 100% per year + bnMidPart, bnRewardPart; + + while (bnLowerBound + CTransaction.nCent <= bnUpperBound) + { + BigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2; + if (nTime < NetInfo.nStakeCurveSwitchTime) + { + // + // Until 20 Oct 2013: reward for coin-year is cut in half every 64x multiply of PoS difficulty + // + // (nRewardCoinYearLimit / nRewardCoinYear) ** 6 == bnProofOfStakeLimit / bnTarget + // + // Human readable form: nRewardCoinYear = 1 / (posdiff ^ 1/6) + // + + bnMidPart = bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue; + bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit; + } + else + { + // + // Since 20 Oct 2013: reward for coin-year is cut in half every 8x multiply of PoS difficulty + // + // (nRewardCoinYearLimit / nRewardCoinYear) ** 3 == bnProofOfStakeLimit / bnTarget + // + // Human readable form: nRewardCoinYear = 1 / (posdiff ^ 1/3) + // + + bnMidPart = bnMidValue * bnMidValue * bnMidValue; + bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit; + } + + if (bnMidPart * bnTargetLimit > bnRewardPart * bnTarget) + bnUpperBound = bnMidValue; + else + bnLowerBound = bnMidValue; + } + + nRewardCoinYear = bnUpperBound; + nRewardCoinYear = Math.Min((nRewardCoinYear / CTransaction.nCent) * CTransaction.nCent, NetInfo.nMaxMintProofOfStake); + } + else + { + // Old creation amount per coin-year, 5% fixed stake mint rate + nRewardCoinYear = 5 * CTransaction.nCent; + } + + nSubsidy = nCoinAge * nRewardCoinYear * 33 / (365 * 33 + 8); + + // Set reasonable reward limit for large inputs since 20 Oct 2013 + // + // This will stimulate large holders to use smaller inputs, that's good for the network protection + if (NetInfo.nStakeCurveSwitchTime < nTime) + { + nSubsidy = Math.Min(nSubsidy, nSubsidyLimit); + } + + return nSubsidy; + } + } }