X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=Novacoin%2FCBlockStore.cs;h=94fc2b67662143d9ae4db9f31ed2190fe2bbf9a5;hb=eb39142ad873e102405ceffb5ec837b510da9096;hp=279edc7245e07706ff6d94658031ba2ee69be05e;hpb=729e4ae7db34bd869fab8079f08968175fe3e5e8;p=NovacoinLibrary.git diff --git a/Novacoin/CBlockStore.cs b/Novacoin/CBlockStore.cs index 279edc7..94fc2b6 100644 --- a/Novacoin/CBlockStore.cs +++ b/Novacoin/CBlockStore.cs @@ -16,7 +16,7 @@ namespace Novacoin /// Block headers table /// [Table("BlockStorage")] - class CBlockStoreItem + public class CBlockStoreItem { /// /// Item ID in the database @@ -61,6 +61,11 @@ namespace Novacoin public uint nNonce { get; set; } /// + /// Next block hash. + /// + public byte[] nextHash { get; set; } + + /// /// Block type flags /// public BlockType BlockTypeFlag { get; set; } @@ -71,9 +76,14 @@ namespace Novacoin public long nStakeModifier { get; set; } /// - /// Stake entropy bit + /// Chain trust score + /// + public byte[] ChainTrust { get; set; } + + /// + /// Proof-of-Stake hash /// - public byte nEntropyBit { get; set; } + public byte[] hashProofOfStake { get; set; } /// /// Block height @@ -207,6 +217,222 @@ namespace Novacoin return false; } } + + /// + /// Previous block cursor + /// + [Ignore] + public CBlockStoreItem prev { + get { return CBlockStore.Instance.GetCursor(prevHash); } + } + + /// + /// Next block cursor + /// + [Ignore] + public CBlockStoreItem next + { + get { return CBlockStore.Instance.GetCursor(nextHash); } + } + + [Ignore] + bool IsInMainChain + { + get { return (next != null); } + } + + /// + /// STake modifier generation flag + /// + [Ignore] + public bool GeneratedStakeModifier + { + get { return (BlockTypeFlag & BlockType.BLOCK_STAKE_MODIFIER) != 0; } + } + + /// + /// Stake entropy bit + /// + [Ignore] + public uint StakeEntropyBit + { + get { return ((uint)(BlockTypeFlag & BlockType.BLOCK_STAKE_ENTROPY) >> 1); } + } + + /// + /// Sets stake modifier and flag. + /// + /// New stake modifier. + /// Set generation flag? + public void SetStakeModifier(long nModifier, bool fGeneratedStakeModifier) + { + nStakeModifier = nModifier; + if (fGeneratedStakeModifier) + BlockTypeFlag |= BlockType.BLOCK_STAKE_MODIFIER; + } + + /// + /// Set entropy bit. + /// + /// Entropy bit value (0 or 1). + /// False if value is our of range. + public bool SetStakeEntropyBit(byte nEntropyBit) + { + if (nEntropyBit > 1) + return false; + BlockTypeFlag |= (nEntropyBit != 0 ? BlockType.BLOCK_STAKE_ENTROPY : 0); + return true; + } + + /// + /// Set proof-of-stake flag. + /// + public void SetProofOfStake() + { + BlockTypeFlag |= BlockType.BLOCK_PROOF_OF_STAKE; + } + + /// + /// Block has no proof-of-stake flag. + /// + [Ignore] + public bool IsProofOfWork + { + get { return (BlockTypeFlag & BlockType.BLOCK_PROOF_OF_STAKE) == 0; } + } + + /// + /// Block has proof-of-stake flag set. + /// + [Ignore] + public bool IsProofOfStake + { + get { return (BlockTypeFlag & BlockType.BLOCK_PROOF_OF_STAKE) != 0; } + } + + /// + /// Chain trust score. + /// + [Ignore] + public uint256 nChainTrust { + get + { + if (ChainTrust.Length != 32) + { + byte[] tmp = ChainTrust; + Array.Resize(ref tmp, 32); + ChainTrust = tmp; + } + + return ChainTrust; + } + set { ChainTrust = Interop.TrimArray(value); } + } + + /// + /// Block trust score. + /// + [Ignore] + public uint256 nBlockTrust + { + get + { + uint256 nTarget = 0; + nTarget.Compact = nBits; + + /* Old protocol */ + if (nTime < NetUtils.nChainChecksSwitchTime) + { + return IsProofOfStake ? (new uint256(1) << 256) / (nTarget + 1) : 1; + } + + /* New protocol */ + + // Calculate work amount for block + var nPoWTrust = NetUtils.nPoWBase / (nTarget + 1); + + // Set nPowTrust to 1 if we are checking PoS block or PoW difficulty is too low + nPoWTrust = (IsProofOfStake || !nPoWTrust) ? 1 : nPoWTrust; + + // Return nPoWTrust for the first 12 blocks + if (prev == null || prev.nHeight < 12) + return nPoWTrust; + + CBlockStoreItem currentIndex = prev; + + if (IsProofOfStake) + { + var nNewTrust = (new uint256(1) << 256) / (nTarget + 1); + + // Return 1/3 of score if parent block is not the PoW block + if (!prev.IsProofOfWork) + { + return nNewTrust / 3; + } + + int nPoWCount = 0; + + // Check last 12 blocks type + while (prev.nHeight - currentIndex.nHeight < 12) + { + if (currentIndex.IsProofOfWork) + { + nPoWCount++; + } + currentIndex = currentIndex.prev; + } + + // Return 1/3 of score if less than 3 PoW blocks found + if (nPoWCount < 3) + { + return nNewTrust / 3; + } + + return nNewTrust; + } + else + { + var nLastBlockTrust = prev.nChainTrust - prev.prev.nChainTrust; + + // Return nPoWTrust + 2/3 of previous block score if two parent blocks are not PoS blocks + if (!prev.IsProofOfStake || !prev.prev.IsProofOfStake) + { + return nPoWTrust + (2 * nLastBlockTrust / 3); + } + + int nPoSCount = 0; + + // Check last 12 blocks type + while (prev.nHeight - currentIndex.nHeight < 12) + { + if (currentIndex.IsProofOfStake) + { + nPoSCount++; + } + currentIndex = currentIndex.prev; + } + + // Return nPoWTrust + 2/3 of previous block score if less than 7 PoS blocks found + if (nPoSCount < 7) + { + return nPoWTrust + (2 * nLastBlockTrust / 3); + } + + nTarget.Compact = prev.nBits; + + if (!nTarget) + { + return 0; + } + + var nNewTrust = (new uint256(1) << 256) / (nTarget + 1); + + // Return nPoWTrust + full trust score for previous block nBits + return nPoWTrust + nNewTrust; + } + } + } + } /// @@ -214,10 +440,9 @@ namespace Novacoin /// public enum BlockType { - PROOF_OF_WORK, - PROOF_OF_WORK_MODIFIER, - PROOF_OF_STAKE, - PROOF_OF_STAKE_MODIFIER + BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block + BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier + BLOCK_STAKE_MODIFIER = (1 << 2), // regenerated stake modifier }; /// @@ -357,6 +582,8 @@ namespace Novacoin fStreamReadWrite = File.Open(strBlockFile, FileMode.OpenOrCreate, FileAccess.ReadWrite); + Instance = this; + if (firstInit) { lock (LockObj) @@ -413,8 +640,6 @@ namespace Novacoin blockMap.TryAdd(item.Hash, item); } } - - Instance = this; } public bool GetTransaction(uint256 TxID, ref CTransaction tx) @@ -443,12 +668,23 @@ namespace Novacoin return false; } - // TODO: compute chain trust, set stake entropy bit, record proof-of-stake hash value + // Compute chain trust score + itemTemplate.nChainTrust = (itemTemplate.prev != null ? itemTemplate.prev.nChainTrust : 0) + itemTemplate.nBlockTrust; + + if (!itemTemplate.SetStakeEntropyBit(Entropy.GetStakeEntropyBit(itemTemplate.nHeight, blockHash))) + { + return false; // SetStakeEntropyBit() failed + } + + // TODO: set stake entropy bit, record proof-of-stake hash value // TODO: compute stake modifier // Add to index - itemTemplate.BlockTypeFlag = block.IsProofOfStake ? BlockType.PROOF_OF_STAKE : BlockType.PROOF_OF_WORK; + if (block.IsProofOfStake) + { + itemTemplate.SetProofOfStake(); + } if (!itemTemplate.WriteToFile(ref writer, ref block)) { @@ -538,7 +774,6 @@ namespace Novacoin var itemTemplate = new CBlockStoreItem() { nHeight = nHeight, - nEntropyBit = Entropy.GetStakeEntropyBit(nHeight, nHash) }; itemTemplate.FillHeader(block.header); @@ -567,6 +802,38 @@ namespace Novacoin return false; } + /// + /// Get block cursor from map. + /// + /// block hash + /// Cursor or null + public CBlockStoreItem GetCursor(uint256 blockHash) + { + if (blockHash == 0) + { + // Genesis block has zero prevHash and no parent. + return null; + } + + // First, check our block map. + CBlockStoreItem item = null; + if (blockMap.TryGetValue(blockHash, out item)) + { + return item; + } + + // Trying to get cursor from the database. + var QueryBlockCursor = dbConn.Query("select * from [BlockStorage] where [Hash] = ?", (byte[])blockHash); + + if (QueryBlockCursor.Count == 1) + { + return QueryBlockCursor[0]; + } + + // Nothing found. + return null; + } + public bool ProcessBlock(ref CBlock block) { var blockHash = block.header.Hash;