From: CryptoManiac Date: Mon, 7 Sep 2015 13:46:03 +0000 (+0300) Subject: Remove interfaces, split database objects into new file. X-Git-Url: https://git.novaco.in/?p=NovacoinLibrary.git;a=commitdiff_plain;h=f02df773a836f8b3df12bd21fd418f7c17b77821 Remove interfaces, split database objects into new file. --- diff --git a/Novacoin/CBlockStore.cs b/Novacoin/CBlockStore.cs index cb07aec..2299e15 100644 --- a/Novacoin/CBlockStore.cs +++ b/Novacoin/CBlockStore.cs @@ -19,774 +19,16 @@ using System; using System.IO; -using System.Linq; using System.Collections.Concurrent; using SQLite.Net; -using SQLite.Net.Attributes; using SQLite.Net.Interop; using SQLite.Net.Platform.Generic; -using SQLiteNetExtensions.Attributes; using System.Collections.Generic; -using System.Diagnostics.Contracts; using System.Text; namespace Novacoin { - [Table("ChainState")] - public class ChainState - { - [PrimaryKey, AutoIncrement] - public long itemId { get; set; } - - /// - /// Hash of top block in the best chain - /// - public byte[] HashBestChain { get; set; } - - /// - /// Total trust score of best chain - /// - public byte[] BestChainTrust { get; set; } - - public uint nBestHeight { get; set;} - - [Ignore] - public uint256 nBestChainTrust - { - get { return BestChainTrust; } - set { BestChainTrust = value; } - } - - [Ignore] - public uint256 nHashBestChain - { - get { return HashBestChain; } - set { HashBestChain = value; } - } - } - - [Table("BlockStorage")] - public class CBlockStoreItem : IBlockStorageItem - { - #region IBlockStorageItem - /// - /// Item ID in the database - /// - [PrimaryKey, AutoIncrement] - public long ItemID { get; set; } - - /// - /// PBKDF2+Salsa20 of block hash - /// - [Unique] - public byte[] Hash { get; set; } - - /// - /// Version of block schema - /// - [Column("nVersion")] - public uint nVersion { get; set; } - - /// - /// Previous block hash. - /// - [Column("prevHash")] - public byte[] prevHash { get; set; } - - /// - /// Merkle root hash. - /// - [Column("merkleRoot")] - public byte[] merkleRoot { get; set; } - - /// - /// Block timestamp. - /// - [Column("nTime")] - public uint nTime { get; set; } - - /// - /// Compressed difficulty representation. - /// - [Column("nBits")] - public uint nBits { get; set; } - - /// - /// Nonce counter. - /// - [Column("nNonce")] - public uint nNonce { get; set; } - - /// - /// Next block hash. - /// - [Column("nextHash")] - public byte[] nextHash { get; set; } - - /// - /// Block type flags - /// - [Column("BlockTypeFlag")] - public BlockType BlockTypeFlag { get; set; } - - /// - /// Stake modifier - /// - [Column("nStakeModifier")] - public long nStakeModifier { get; set; } - - /// - /// Proof-of-Stake hash - /// - [Column("hashProofOfStake")] - public byte[] hashProofOfStake { get; set; } - - /// - /// Stake generation outpoint. - /// - [Column("prevoutStake")] - public byte[] prevoutStake { get; set; } - - /// - /// Stake generation time. - /// - [Column("nStakeTime")] - public uint nStakeTime { get; set; } - - /// - /// Block height, encoded in VarInt format - /// - [Column("nHeight")] - public uint nHeight { get; set; } - - /// - /// Chain trust score, serialized and trimmed uint256 representation. - /// - [Column("ChainTrust")] - public byte[] ChainTrust { get; set; } - - /// - /// Block position in file, encoded in VarInt format - /// - [Column("BlockPos")] - public byte[] BlockPos { get; set; } - - /// - /// Block size in bytes, encoded in VarInt format - /// - [Column("BlockSize")] - public byte[] BlockSize { get; set; } - #endregion - - /// - /// Accessor and mutator for BlockPos value. - /// - [Ignore] - public long nBlockPos - { - get { return (long)VarInt.DecodeVarInt(BlockPos); } - set { BlockPos = VarInt.EncodeVarInt(value); } - } - - /// - /// Accessor and mutator for BlockSize value. - /// - [Ignore] - public int nBlockSize - { - get { return (int)VarInt.DecodeVarInt(BlockSize); } - set { BlockSize = VarInt.EncodeVarInt(value); } - } - - /// - /// Fill database item with data from given block header. - /// - /// Block header - /// Header hash - public uint256 FillHeader(CBlockHeader header) - { - uint256 _hash = header.Hash; - - Hash = _hash; - - nVersion = header.nVersion; - prevHash = header.prevHash; - merkleRoot = header.merkleRoot; - nTime = header.nTime; - nBits = header.nBits; - nNonce = header.nNonce; - - return _hash; - } - - /// - /// Reconstruct block header from item data. - /// - public CBlockHeader BlockHeader - { - get - { - CBlockHeader header = new CBlockHeader(); - - header.nVersion = nVersion; - header.prevHash = prevHash; - header.merkleRoot = merkleRoot; - header.nTime = nTime; - header.nBits = nBits; - header.nNonce = nNonce; - - return header; - } - } - - /// - /// Read block from file. - /// - /// Stream with read access. - /// CBlock reference. - /// Result - public bool ReadFromFile(ref Stream reader, out CBlock block) - { - var buffer = new byte[nBlockSize]; - block = null; - - try - { - reader.Seek(nBlockPos, SeekOrigin.Begin); - - if (nBlockSize != reader.Read(buffer, 0, nBlockSize)) - { - return false; - } - - block = new CBlock(buffer); - - return true; - } - catch (IOException) - { - // I/O error - return false; - } - catch (BlockException) - { - // Constructor exception - return false; - } - } - - /// - /// Writes given block to file and prepares cursor object for insertion into the database. - /// - /// Stream with write access. - /// CBlock reference. - /// Result - public bool WriteToFile(ref Stream writer, ref CBlock block) - { - try - { - byte[] blockBytes = block; - - var magicBytes = BitConverter.GetBytes(CBlockStore.nMagicNumber); - var blkLenBytes = BitConverter.GetBytes(blockBytes.Length); - - // Seek to the end and then append magic bytes there. - writer.Seek(0, SeekOrigin.End); - writer.Write(magicBytes, 0, magicBytes.Length); - writer.Write(blkLenBytes, 0, blkLenBytes.Length); - - // Save block size and current position in the block cursor fields. - nBlockPos = writer.Position; - nBlockSize = blockBytes.Length; - - // Write block and flush the stream. - writer.Write(blockBytes, 0, blockBytes.Length); - writer.Flush(); - - return true; - } - catch (IOException) - { - // I/O error - return false; - } - catch (Exception) - { - // Some serialization error - return false; - } - } - - /// - /// Previous block cursor - /// - [Ignore] - public CBlockStoreItem prev - { - get { return CBlockStore.Instance.GetMapCursor(prevHash); } - } - - /// - /// Next block cursor - /// - [Ignore] - public CBlockStoreItem next - { - get - { - if (nextHash == null) - { - return null; - } - - return CBlockStore.Instance.GetMapCursor(nextHash); - } - set - { - nextHash = value.Hash; - - CBlockStore.Instance.UpdateMapCursor(this); - } - } - - [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; } - } - - /// - /// Block trust score. - /// - [Ignore] - public uint256 nBlockTrust - { - get - { - uint256 nTarget = 0; - nTarget.Compact = nBits; - - /* Old protocol */ - if (nTime < NetInfo.nChainChecksSwitchTime) - { - return IsProofOfStake ? (new uint256(1) << 256) / (nTarget + 1) : 1; - } - - /* New protocol */ - - // Calculate work amount for block - var nPoWTrust = NetInfo.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; - } - } - } - - /// - /// Stake modifier checksum. - /// - public uint nStakeModifierChecksum; - - /// - /// Chain trust score - /// - [Ignore] - public uint256 nChainTrust { - get { return Interop.AppendWithZeros(ChainTrust); } - set { ChainTrust = Interop.TrimArray(value); } - } - - public long nMint { get; internal set; } - public long nMoneySupply { get; internal set; } - } - - /// - /// Block type. - /// - public enum BlockType - { - 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 - }; - - /// - /// Transaction type. - /// - public enum TxFlags : byte - { - TX_COINBASE, - TX_COINSTAKE, - TX_USER - } - - /// - /// Output flags. - /// - public enum OutputFlags : byte - { - AVAILABLE, // Unspent output - SPENT // Spent output - } - - [Table("MerkleNodes")] - public class CMerkleNode : IMerkleNode - { - #region IMerkleNode - /// - /// Node identifier - /// - [PrimaryKey, AutoIncrement] - public long nMerkleNodeID { get; set; } - - /// - /// Reference to parent block database item. - /// - [ForeignKey(typeof(CBlockStoreItem), Name = "ItemId")] - public long nParentBlockID { get; set; } - - /// - /// Transaction type flag - /// - [Column("TransactionFlags")] - public TxFlags TransactionFlags { get; set; } - - /// - /// Transaction hash - /// - [Column("TransactionHash")] - public byte[] TransactionHash { get; set; } - - /// - /// Transaction offset from the beginning of block header, encoded in VarInt format. - /// - [Column("TxOffset")] - public byte[] TxOffset { get; set; } - - /// - /// Transaction size, encoded in VarInt format. - /// - [Column("TxSize")] - public byte[] TxSize { get; set; } - #endregion - - /// - /// Read transaction from file. - /// - /// Stream with read access. - /// CTransaction reference. - /// Result - public bool ReadFromFile(ref Stream reader, long nBlockPos, out CTransaction tx) - { - var buffer = new byte[CTransaction.nMaxTxSize]; - - tx = null; - - try - { - reader.Seek(nBlockPos + nTxOffset, SeekOrigin.Begin); // Seek to transaction offset - - if (nTxSize != reader.Read(buffer, 0, nTxSize)) - { - return false; - } - - tx = new CTransaction(buffer); - - return true; - } - catch (IOException) - { - // I/O error - return false; - } - catch (TransactionConstructorException) - { - // Constructor error - return false; - } - } - - /// - /// Transaction offset accessor - /// - [Ignore] - public long nTxOffset - { - get { return (long) VarInt.DecodeVarInt(TxOffset); } - private set { TxOffset = VarInt.EncodeVarInt(value); } - } - - /// - /// Transaction size accessor - /// - [Ignore] - public int nTxSize - { - get { return (int)VarInt.DecodeVarInt(TxSize); } - private set { TxSize = VarInt.EncodeVarInt(value); } - } - - public CMerkleNode(CTransaction tx) - { - nTxOffset = -1; - nParentBlockID = -1; - - nTxSize = tx.Size; - TransactionHash = tx.Hash; - - if (tx.IsCoinBase) - { - TransactionFlags |= TxFlags.TX_COINBASE; - } - else if (tx.IsCoinStake) - { - TransactionFlags |= TxFlags.TX_COINSTAKE; - } - else - { - TransactionFlags |= TxFlags.TX_USER; - } - } - - public CMerkleNode(long nBlockId, long nOffset, CTransaction tx) - { - nParentBlockID = nBlockId; - - nTxOffset = nOffset; - nTxSize = tx.Size; - TransactionHash = tx.Hash; - - if (tx.IsCoinBase) - { - TransactionFlags |= TxFlags.TX_COINBASE; - } - else if (tx.IsCoinStake) - { - TransactionFlags |= TxFlags.TX_COINSTAKE; - } - else - { - TransactionFlags |= TxFlags.TX_USER; - } - } - - } - - [Table("Outputs")] - public class TxOutItem : ITxOutItem - { - /// - /// Reference to transaction item. - /// - [ForeignKey(typeof(CMerkleNode), Name = "nMerkleNodeID")] - public long nMerkleNodeID { get; set; } - - /// - /// Output flags - /// - public OutputFlags outputFlags { get; set; } - - /// - /// Output number in VarInt format. - /// - public byte[] OutputNumber { get; set; } - - /// - /// Output value in VarInt format. - /// - public byte[] OutputValue { get; set; } - - /// - /// Second half of script which contains spending instructions. - /// - public byte[] scriptPubKey { get; set; } - - /// - /// Getter for output number. - /// - [Ignore] - public uint nOut - { - get { return (uint)VarInt.DecodeVarInt(OutputNumber); } - private set { OutputNumber = VarInt.EncodeVarInt(value); } - } - - /// - /// Getter for output value. - /// - [Ignore] - public ulong nValue - { - get { return VarInt.DecodeVarInt(OutputValue); } - private set { OutputValue = VarInt.EncodeVarInt(value); } - } - - /// - /// Getter ans setter for IsSpent flag. - /// - [Ignore] - public bool IsSpent - { - get { return (outputFlags & OutputFlags.SPENT) != 0; } - set { outputFlags |= value ? OutputFlags.SPENT : OutputFlags.AVAILABLE; } - } - - public TxOutItem(CTxOut o, uint nOut) - { - nValue = o.nValue; - scriptPubKey = o.scriptPubKey; - this.nOut = nOut; - } - } - public class CBlockStore : IDisposable { public const uint nMagicNumber = 0xe5e9e8e4; @@ -983,10 +225,6 @@ namespace Novacoin return false; } - interface InputsJoin : ITxOutItem - { - byte[] TransactionHash { get; set; } - } public bool FetchInputs(CTransaction tx, ref Dictionary queued, ref Dictionary inputs, bool IsBlock, out bool Invalid) { @@ -1468,7 +706,12 @@ namespace Novacoin queuedMerkleNodes.Add(hashTx, mNode); var outKey = new COutPoint(hashTx, i); - var outData = new TxOutItem(tx.vout[i], i); + var outData = new TxOutItem(); + + outData.nValue = tx.vout[i].nValue; + outData.scriptPubKey = tx.vout[i].scriptPubKey; + outData.nOut = i; + outData.IsSpent = false; @@ -1671,40 +914,22 @@ namespace Novacoin return cursor.ReadFromFile(ref fStreamReadWrite, out block); } - - /// - /// Interface for join - /// - interface IBlockJoinMerkle : IBlockStorageItem, IMerkleNode - { - } - /// /// Get block and transaction by transaction hash. /// /// Transaction hash /// Block reference - /// Transaction reference /// Block position reference - /// Transaction position reference /// Result of operation - public bool GetBlockByTransactionID(uint256 TxID, ref CBlock block, ref CTransaction tx, ref long nBlockPos, ref long nTxPos) + public bool GetBlockByTransactionID(uint256 TxID, ref CBlock block, ref long nBlockPos) { - var queryResult = dbConn.Query("select * from [BlockStorage] b left join [MerkleNodes] m on (b.[ItemID] = m.[nParentBlockID]) where m.[TransactionHash] = ?", (byte[])TxID); + var queryResult = dbConn.Query("select b.* from [BlockStorage] b left join [MerkleNodes] m on (b.[ItemID] = m.[nParentBlockID]) where m.[TransactionHash] = ?", (byte[])TxID); if (queryResult.Count == 1) { - CBlockStoreItem blockCursor = (CBlockStoreItem) queryResult[0]; - CMerkleNode txCursor = (CMerkleNode)queryResult[0]; - - var reader = new BinaryReader(fStreamReadWrite).BaseStream; - - if (!txCursor.ReadFromFile(ref reader, blockCursor.nBlockPos, out tx)) - { - return false; // Unable to read transaction - } + CBlockStoreItem blockCursor = queryResult[0]; - return blockCursor.ReadFromFile(ref reader, out block); + return blockCursor.ReadFromFile(ref fStreamReadWrite, out block); } // Tx not found diff --git a/Novacoin/DatabaseInterfaces.cs b/Novacoin/DatabaseInterfaces.cs deleted file mode 100644 index 0d6e35e..0000000 --- a/Novacoin/DatabaseInterfaces.cs +++ /dev/null @@ -1,182 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using SQLite.Net; -using SQLite.Net.Attributes; -using SQLite.Net.Interop; -using SQLite.Net.Platform.Generic; -using SQLiteNetExtensions.Attributes; -using System.Diagnostics.Contracts; - -namespace Novacoin -{ - /// - /// Block headers table - /// - public interface IBlockStorageItem - { - /// - /// Item ID in the database - /// - long ItemID { get; set; } - - /// - /// PBKDF2+Salsa20 of block hash - /// - byte[] Hash { get; set; } - - /// - /// Version of block schema - /// - uint nVersion { get; set; } - - /// - /// Previous block hash. - /// - byte[] prevHash { get; set; } - - /// - /// Merkle root hash. - /// - byte[] merkleRoot { get; set; } - - /// - /// Block timestamp. - /// - uint nTime { get; set; } - - /// - /// Compressed difficulty representation. - /// - uint nBits { get; set; } - - /// - /// Nonce counter. - /// - uint nNonce { get; set; } - - /// - /// Next block hash. - /// - byte[] nextHash { get; set; } - - /// - /// Block type flags - /// - BlockType BlockTypeFlag { get; set; } - - /// - /// Stake modifier - /// - long nStakeModifier { get; set; } - - /// - /// Proof-of-Stake hash - /// - byte[] hashProofOfStake { get; set; } - - /// - /// Stake generation outpoint. - /// - byte[] prevoutStake { get; set; } - - /// - /// Stake generation time. - /// - uint nStakeTime { get; set; } - - /// - /// Block height - /// - uint nHeight { get; set; } - - /// - /// Block position in file - /// - byte[] BlockPos { get; set; } - - /// - /// Block size in bytes - /// - byte[] BlockSize { get; set; } - }; - - public interface IMerkleNode - { - /// - /// Node identifier - /// - long nMerkleNodeID { get; set; } - - /// - /// Reference to parent block database item. - /// - long nParentBlockID { get; set; } - - /// - /// Transaction type flag - /// - TxFlags TransactionFlags { get; set; } - - /// - /// Transaction hash - /// - byte[] TransactionHash { get; set; } - - /// - /// Transaction offset from the beginning of block header, encoded in VarInt format. - /// - byte[] TxOffset { get; set; } - - /// - /// Transaction size, encoded in VarInt format. - /// - byte[] TxSize { get; set; } - } - - public interface ITxOutItem - { - /// - /// Reference to transaction item. - /// - long nMerkleNodeID { get; set; } - - /// - /// Output flags - /// - OutputFlags outputFlags { get; set; } - - /// - /// Output number in VarInt format. - /// - byte[] OutputNumber { get; set; } - - /// - /// Output value in VarInt format. - /// - byte[] OutputValue { get; set; } - - /// - /// Second half of script which contains spending instructions. - /// - byte[] scriptPubKey { get; set; } - - /// - /// Getter for output number. - /// - uint nOut { get; } - - /// - /// Getter for output value. - /// - ulong nValue { get; } - - /// - /// Getter ans setter for IsSpent flag. - /// - bool IsSpent { get; set; } - } -} diff --git a/Novacoin/DatabaseObjects.cs b/Novacoin/DatabaseObjects.cs new file mode 100644 index 0000000..b49d63e --- /dev/null +++ b/Novacoin/DatabaseObjects.cs @@ -0,0 +1,767 @@ +using SQLite.Net.Attributes; +using SQLiteNetExtensions.Attributes; +using System; +using System.IO; + +namespace Novacoin +{ + [Table("ChainState")] + public class ChainState + { + [PrimaryKey, AutoIncrement] + public long itemId { get; set; } + + /// + /// Hash of top block in the best chain + /// + public byte[] HashBestChain { get; set; } + + /// + /// Total trust score of best chain + /// + public byte[] BestChainTrust { get; set; } + + public uint nBestHeight { get; set; } + + [Ignore] + public uint256 nBestChainTrust + { + get { return BestChainTrust; } + set { BestChainTrust = value; } + } + + [Ignore] + public uint256 nHashBestChain + { + get { return HashBestChain; } + set { HashBestChain = value; } + } + } + + [Table("BlockStorage")] + public class CBlockStoreItem + { + #region IBlockStorageItem + /// + /// Item ID in the database + /// + [PrimaryKey, AutoIncrement] + public long ItemID { get; set; } + + /// + /// PBKDF2+Salsa20 of block hash + /// + [Unique] + public byte[] Hash { get; set; } + + /// + /// Version of block schema + /// + [Column("nVersion")] + public uint nVersion { get; set; } + + /// + /// Previous block hash. + /// + [Column("prevHash")] + public byte[] prevHash { get; set; } + + /// + /// Merkle root hash. + /// + [Column("merkleRoot")] + public byte[] merkleRoot { get; set; } + + /// + /// Block timestamp. + /// + [Column("nTime")] + public uint nTime { get; set; } + + /// + /// Compressed difficulty representation. + /// + [Column("nBits")] + public uint nBits { get; set; } + + /// + /// Nonce counter. + /// + [Column("nNonce")] + public uint nNonce { get; set; } + + /// + /// Next block hash. + /// + [Column("nextHash")] + public byte[] nextHash { get; set; } + + /// + /// Block type flags + /// + [Column("BlockTypeFlag")] + public BlockType BlockTypeFlag { get; set; } + + /// + /// Stake modifier + /// + [Column("nStakeModifier")] + public long nStakeModifier { get; set; } + + /// + /// Proof-of-Stake hash + /// + [Column("hashProofOfStake")] + public byte[] hashProofOfStake { get; set; } + + /// + /// Stake generation outpoint. + /// + [Column("prevoutStake")] + public byte[] prevoutStake { get; set; } + + /// + /// Stake generation time. + /// + [Column("nStakeTime")] + public uint nStakeTime { get; set; } + + /// + /// Block height, encoded in VarInt format + /// + [Column("nHeight")] + public uint nHeight { get; set; } + + /// + /// Chain trust score, serialized and trimmed uint256 representation. + /// + [Column("ChainTrust")] + public byte[] ChainTrust { get; set; } + + /// + /// Block position in file, encoded in VarInt format + /// + [Column("BlockPos")] + public byte[] BlockPos { get; set; } + + /// + /// Block size in bytes, encoded in VarInt format + /// + [Column("BlockSize")] + public byte[] BlockSize { get; set; } + #endregion + + /// + /// Accessor and mutator for BlockPos value. + /// + [Ignore] + public long nBlockPos + { + get { return (long)VarInt.DecodeVarInt(BlockPos); } + set { BlockPos = VarInt.EncodeVarInt(value); } + } + + /// + /// Accessor and mutator for BlockSize value. + /// + [Ignore] + public int nBlockSize + { + get { return (int)VarInt.DecodeVarInt(BlockSize); } + set { BlockSize = VarInt.EncodeVarInt(value); } + } + + /// + /// Fill database item with data from given block header. + /// + /// Block header + /// Header hash + public uint256 FillHeader(CBlockHeader header) + { + uint256 _hash = header.Hash; + + Hash = _hash; + + nVersion = header.nVersion; + prevHash = header.prevHash; + merkleRoot = header.merkleRoot; + nTime = header.nTime; + nBits = header.nBits; + nNonce = header.nNonce; + + return _hash; + } + + /// + /// Reconstruct block header from item data. + /// + public CBlockHeader BlockHeader + { + get + { + CBlockHeader header = new CBlockHeader(); + + header.nVersion = nVersion; + header.prevHash = prevHash; + header.merkleRoot = merkleRoot; + header.nTime = nTime; + header.nBits = nBits; + header.nNonce = nNonce; + + return header; + } + } + + /// + /// Read block from file. + /// + /// Stream with read access. + /// CBlock reference. + /// Result + public bool ReadFromFile(ref Stream reader, out CBlock block) + { + var buffer = new byte[nBlockSize]; + block = null; + + try + { + reader.Seek(nBlockPos, SeekOrigin.Begin); + + if (nBlockSize != reader.Read(buffer, 0, nBlockSize)) + { + return false; + } + + block = new CBlock(buffer); + + return true; + } + catch (IOException) + { + // I/O error + return false; + } + catch (BlockException) + { + // Constructor exception + return false; + } + } + + /// + /// Writes given block to file and prepares cursor object for insertion into the database. + /// + /// Stream with write access. + /// CBlock reference. + /// Result + public bool WriteToFile(ref Stream writer, ref CBlock block) + { + try + { + byte[] blockBytes = block; + + var magicBytes = BitConverter.GetBytes(CBlockStore.nMagicNumber); + var blkLenBytes = BitConverter.GetBytes(blockBytes.Length); + + // Seek to the end and then append magic bytes there. + writer.Seek(0, SeekOrigin.End); + writer.Write(magicBytes, 0, magicBytes.Length); + writer.Write(blkLenBytes, 0, blkLenBytes.Length); + + // Save block size and current position in the block cursor fields. + nBlockPos = writer.Position; + nBlockSize = blockBytes.Length; + + // Write block and flush the stream. + writer.Write(blockBytes, 0, blockBytes.Length); + writer.Flush(); + + return true; + } + catch (IOException) + { + // I/O error + return false; + } + catch (Exception) + { + // Some serialization error + return false; + } + } + + /// + /// Previous block cursor + /// + [Ignore] + public CBlockStoreItem prev + { + get { return CBlockStore.Instance.GetMapCursor(prevHash); } + } + + /// + /// Next block cursor + /// + [Ignore] + public CBlockStoreItem next + { + get + { + if (nextHash == null) + { + return null; + } + + return CBlockStore.Instance.GetMapCursor(nextHash); + } + set + { + nextHash = value.Hash; + + CBlockStore.Instance.UpdateMapCursor(this); + } + } + + [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; } + } + + /// + /// Block trust score. + /// + [Ignore] + public uint256 nBlockTrust + { + get + { + uint256 nTarget = 0; + nTarget.Compact = nBits; + + /* Old protocol */ + if (nTime < NetInfo.nChainChecksSwitchTime) + { + return IsProofOfStake ? (new uint256(1) << 256) / (nTarget + 1) : 1; + } + + /* New protocol */ + + // Calculate work amount for block + var nPoWTrust = NetInfo.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; + } + } + } + + /// + /// Stake modifier checksum. + /// + public uint nStakeModifierChecksum; + + /// + /// Chain trust score + /// + [Ignore] + public uint256 nChainTrust + { + get { return Interop.AppendWithZeros(ChainTrust); } + set { ChainTrust = Interop.TrimArray(value); } + } + + public long nMint { get; internal set; } + public long nMoneySupply { get; internal set; } + } + + /// + /// Block type. + /// + public enum BlockType + { + 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 + }; + + /// + /// Transaction type. + /// + public enum TxFlags : byte + { + TX_COINBASE, + TX_COINSTAKE, + TX_USER + } + + /// + /// Output flags. + /// + public enum OutputFlags : byte + { + AVAILABLE, // Unspent output + SPENT // Spent output + } + + [Table("MerkleNodes")] + public class CMerkleNode + { + #region IMerkleNode + /// + /// Node identifier + /// + [PrimaryKey, AutoIncrement] + public long nMerkleNodeID { get; set; } + + /// + /// Reference to parent block database item. + /// + [ForeignKey(typeof(CBlockStoreItem), Name = "ItemId")] + public long nParentBlockID { get; set; } + + /// + /// Transaction type flag + /// + [Column("TransactionFlags")] + public TxFlags TransactionFlags { get; set; } + + /// + /// Transaction hash + /// + [Column("TransactionHash")] + public byte[] TransactionHash { get; set; } + + /// + /// Transaction offset from the beginning of block header, encoded in VarInt format. + /// + [Column("TxOffset")] + public byte[] TxOffset { get; set; } + + /// + /// Transaction size, encoded in VarInt format. + /// + [Column("TxSize")] + public byte[] TxSize { get; set; } + #endregion + + /// + /// Read transaction from file. + /// + /// Stream with read access. + /// CTransaction reference. + /// Result + public bool ReadFromFile(ref Stream reader, long nBlockPos, out CTransaction tx) + { + var buffer = new byte[CTransaction.nMaxTxSize]; + + tx = null; + + try + { + reader.Seek(nBlockPos + nTxOffset, SeekOrigin.Begin); // Seek to transaction offset + + if (nTxSize != reader.Read(buffer, 0, nTxSize)) + { + return false; + } + + tx = new CTransaction(buffer); + + return true; + } + catch (IOException) + { + // I/O error + return false; + } + catch (TransactionConstructorException) + { + // Constructor error + return false; + } + } + + /// + /// Transaction offset accessor + /// + [Ignore] + public long nTxOffset + { + get { return (long)VarInt.DecodeVarInt(TxOffset); } + private set { TxOffset = VarInt.EncodeVarInt(value); } + } + + /// + /// Transaction size accessor + /// + [Ignore] + public int nTxSize + { + get { return (int)VarInt.DecodeVarInt(TxSize); } + private set { TxSize = VarInt.EncodeVarInt(value); } + } + + public CMerkleNode(CTransaction tx) + { + nTxOffset = -1; + nParentBlockID = -1; + + nTxSize = tx.Size; + TransactionHash = tx.Hash; + + if (tx.IsCoinBase) + { + TransactionFlags |= TxFlags.TX_COINBASE; + } + else if (tx.IsCoinStake) + { + TransactionFlags |= TxFlags.TX_COINSTAKE; + } + else + { + TransactionFlags |= TxFlags.TX_USER; + } + } + + public CMerkleNode(long nBlockId, long nOffset, CTransaction tx) + { + nParentBlockID = nBlockId; + + nTxOffset = nOffset; + nTxSize = tx.Size; + TransactionHash = tx.Hash; + + if (tx.IsCoinBase) + { + TransactionFlags |= TxFlags.TX_COINBASE; + } + else if (tx.IsCoinStake) + { + TransactionFlags |= TxFlags.TX_COINSTAKE; + } + else + { + TransactionFlags |= TxFlags.TX_USER; + } + } + + } + + [Table("Outputs")] + public class TxOutItem + { + /// + /// Reference to transaction item. + /// + [ForeignKey(typeof(CMerkleNode), Name = "nMerkleNodeID")] + public long nMerkleNodeID { get; set; } + + /// + /// Output flags + /// + public OutputFlags outputFlags { get; set; } + + /// + /// Output number in VarInt format. + /// + public byte[] OutputNumber { get; set; } + + /// + /// Output value in VarInt format. + /// + public byte[] OutputValue { get; set; } + + /// + /// Second half of script which contains spending instructions. + /// + public byte[] scriptPubKey { get; set; } + + /// + /// Getter for output number. + /// + [Ignore] + public uint nOut + { + get { return (uint)VarInt.DecodeVarInt(OutputNumber); } + set { OutputNumber = VarInt.EncodeVarInt(value); } + } + + /// + /// Getter for output value. + /// + [Ignore] + public ulong nValue + { + get { return VarInt.DecodeVarInt(OutputValue); } + set { OutputValue = VarInt.EncodeVarInt(value); } + } + + /// + /// Getter ans setter for IsSpent flag. + /// + [Ignore] + public bool IsSpent + { + get { return (outputFlags & OutputFlags.SPENT) != 0; } + set { outputFlags |= value ? OutputFlags.SPENT : OutputFlags.AVAILABLE; } + } + } + + + /// + /// TxOut + transaction hash + /// + public class InputsJoin : TxOutItem + { + public byte[] TransactionHash { get; set; } + } + + + +} diff --git a/Novacoin/Novacoin.csproj b/Novacoin/Novacoin.csproj index 2954c91..b0a0838 100644 --- a/Novacoin/Novacoin.csproj +++ b/Novacoin/Novacoin.csproj @@ -110,7 +110,7 @@ - + diff --git a/Novacoin/StakeModifier.cs b/Novacoin/StakeModifier.cs index b3f7876..944fd36 100644 --- a/Novacoin/StakeModifier.cs +++ b/Novacoin/StakeModifier.cs @@ -443,16 +443,34 @@ namespace Novacoin CTxIn txin = tx.vin[0]; // Read block header - + + long nBlockPos = 0; CBlock block = null; - CTransaction txPrev = null; - long nBlockPos = 0, nTxPos = 0; - - if (!CBlockStore.Instance.GetBlockByTransactionID(txin.prevout.hash, ref block, ref txPrev, ref nBlockPos, ref nTxPos)) + if (!CBlockStore.Instance.GetBlockByTransactionID(txin.prevout.hash, ref block, ref 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