2 * Novacoin classes library
3 * Copyright (C) 2015 Alex D. (balthazar.ad@gmail.com)
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 using System.Collections.Concurrent;
26 using SQLite.Net.Attributes;
27 using SQLite.Net.Interop;
28 using SQLite.Net.Platform.Generic;
29 using SQLiteNetExtensions.Attributes;
30 using System.Collections.Generic;
31 using System.Diagnostics.Contracts;
36 /// Block headers table
38 [Table("BlockStorage")]
39 public class CBlockStoreItem
42 /// Item ID in the database
44 [PrimaryKey, AutoIncrement]
45 public long ItemID { get; set; }
48 /// PBKDF2+Salsa20 of block hash
51 public byte[] Hash { get; set; }
54 /// Version of block schema
56 public uint nVersion { get; set; }
59 /// Previous block hash.
61 public byte[] prevHash { get; set; }
66 public byte[] merkleRoot { get; set; }
71 public uint nTime { get; set; }
74 /// Compressed difficulty representation.
76 public uint nBits { get; set; }
81 public uint nNonce { get; set; }
86 public byte[] nextHash { get; set; }
91 public BlockType BlockTypeFlag { get; set; }
96 public long nStakeModifier { get; set; }
99 /// Proof-of-Stake hash
101 public byte[] hashProofOfStake { get; set; }
104 /// Stake generation outpoint.
106 public byte[] prevoutStake { get; set; }
109 /// Stake generation time.
111 public uint nStakeTime { get; set; }
116 public uint nHeight { get; set; }
119 /// Block position in file
121 public long nBlockPos { get; set; }
124 /// Block size in bytes
126 public int nBlockSize { get; set; }
129 /// Fill database item with data from given block header.
131 /// <param name="header">Block header</param>
132 /// <returns>Header hash</returns>
133 public uint256 FillHeader(CBlockHeader header)
136 Hash = _hash = header.Hash;
138 nVersion = header.nVersion;
139 prevHash = header.prevHash;
140 merkleRoot = header.merkleRoot;
141 nTime = header.nTime;
142 nBits = header.nBits;
143 nNonce = header.nNonce;
149 /// Reconstruct block header from item data.
151 public CBlockHeader BlockHeader
155 CBlockHeader header = new CBlockHeader();
157 header.nVersion = nVersion;
158 header.prevHash = prevHash;
159 header.merkleRoot = merkleRoot;
160 header.nTime = nTime;
161 header.nBits = nBits;
162 header.nNonce = nNonce;
169 /// Read block from file.
171 /// <param name="reader">Stream with read access.</param>
172 /// <param name="reader">CBlock reference.</param>
173 /// <returns>Result</returns>
174 public bool ReadFromFile(ref Stream reader, out CBlock block)
176 var buffer = new byte[nBlockSize];
181 reader.Seek(nBlockPos, SeekOrigin.Begin);
183 if (nBlockSize != reader.Read(buffer, 0, nBlockSize))
188 block = new CBlock(buffer);
197 catch (BlockException)
199 // Constructor exception
205 /// Writes given block to file and prepares cursor object for insertion into the database.
207 /// <param name="writer">Stream with write access.</param>
208 /// <param name="block">CBlock reference.</param>
209 /// <returns>Result</returns>
210 public bool WriteToFile(ref Stream writer, ref CBlock block)
214 byte[] blockBytes = block;
216 var magicBytes = BitConverter.GetBytes(CBlockStore.nMagicNumber);
217 var blkLenBytes = BitConverter.GetBytes(blockBytes.Length);
219 // Seek to the end and then append magic bytes there.
220 writer.Seek(0, SeekOrigin.End);
221 writer.Write(magicBytes, 0, magicBytes.Length);
222 writer.Write(blkLenBytes, 0, blkLenBytes.Length);
224 // Save block size and current position in the block cursor fields.
225 nBlockPos = writer.Position;
226 nBlockSize = blockBytes.Length;
228 // Write block and flush the stream.
229 writer.Write(blockBytes, 0, blockBytes.Length);
241 // Some serialization error
247 /// Previous block cursor
250 public CBlockStoreItem prev {
251 get { return CBlockStore.Instance.GetCursor(prevHash); }
255 /// Next block cursor
258 public CBlockStoreItem next
260 get { return CBlockStore.Instance.GetCursor(nextHash); }
263 CBlockStoreItem newCursor = this;
264 newCursor.nextHash = value.Hash;
266 CBlockStore.Instance.UpdateCursor(this, ref newCursor);
273 get { return (next != null); }
277 /// STake modifier generation flag
280 public bool GeneratedStakeModifier
282 get { return (BlockTypeFlag & BlockType.BLOCK_STAKE_MODIFIER) != 0; }
286 /// Stake entropy bit
289 public uint StakeEntropyBit
291 get { return ((uint)(BlockTypeFlag & BlockType.BLOCK_STAKE_ENTROPY) >> 1); }
295 /// Sets stake modifier and flag.
297 /// <param name="nModifier">New stake modifier.</param>
298 /// <param name="fGeneratedStakeModifier">Set generation flag?</param>
299 public void SetStakeModifier(long nModifier, bool fGeneratedStakeModifier)
301 nStakeModifier = nModifier;
302 if (fGeneratedStakeModifier)
303 BlockTypeFlag |= BlockType.BLOCK_STAKE_MODIFIER;
309 /// <param name="nEntropyBit">Entropy bit value (0 or 1).</param>
310 /// <returns>False if value is our of range.</returns>
311 public bool SetStakeEntropyBit(byte nEntropyBit)
315 BlockTypeFlag |= (nEntropyBit != 0 ? BlockType.BLOCK_STAKE_ENTROPY : 0);
320 /// Set proof-of-stake flag.
322 public void SetProofOfStake()
324 BlockTypeFlag |= BlockType.BLOCK_PROOF_OF_STAKE;
328 /// Block has no proof-of-stake flag.
331 public bool IsProofOfWork
333 get { return (BlockTypeFlag & BlockType.BLOCK_PROOF_OF_STAKE) == 0; }
337 /// Block has proof-of-stake flag set.
340 public bool IsProofOfStake
342 get { return (BlockTypeFlag & BlockType.BLOCK_PROOF_OF_STAKE) != 0; }
346 /// Block trust score.
349 public uint256 nBlockTrust
354 nTarget.Compact = nBits;
357 if (nTime < NetUtils.nChainChecksSwitchTime)
359 return IsProofOfStake ? (new uint256(1) << 256) / (nTarget + 1) : 1;
364 // Calculate work amount for block
365 var nPoWTrust = NetUtils.nPoWBase / (nTarget + 1);
367 // Set nPowTrust to 1 if we are checking PoS block or PoW difficulty is too low
368 nPoWTrust = (IsProofOfStake || !nPoWTrust) ? 1 : nPoWTrust;
370 // Return nPoWTrust for the first 12 blocks
371 if (prev == null || prev.nHeight < 12)
374 CBlockStoreItem currentIndex = prev;
378 var nNewTrust = (new uint256(1) << 256) / (nTarget + 1);
380 // Return 1/3 of score if parent block is not the PoW block
381 if (!prev.IsProofOfWork)
383 return nNewTrust / 3;
388 // Check last 12 blocks type
389 while (prev.nHeight - currentIndex.nHeight < 12)
391 if (currentIndex.IsProofOfWork)
395 currentIndex = currentIndex.prev;
398 // Return 1/3 of score if less than 3 PoW blocks found
401 return nNewTrust / 3;
408 var nLastBlockTrust = prev.nChainTrust - prev.prev.nChainTrust;
410 // Return nPoWTrust + 2/3 of previous block score if two parent blocks are not PoS blocks
411 if (!prev.IsProofOfStake || !prev.prev.IsProofOfStake)
413 return nPoWTrust + (2 * nLastBlockTrust / 3);
418 // Check last 12 blocks type
419 while (prev.nHeight - currentIndex.nHeight < 12)
421 if (currentIndex.IsProofOfStake)
425 currentIndex = currentIndex.prev;
428 // Return nPoWTrust + 2/3 of previous block score if less than 7 PoS blocks found
431 return nPoWTrust + (2 * nLastBlockTrust / 3);
434 nTarget.Compact = prev.nBits;
441 var nNewTrust = (new uint256(1) << 256) / (nTarget + 1);
443 // Return nPoWTrust + full trust score for previous block nBits
444 return nPoWTrust + nNewTrust;
450 /// Stake modifier checksum.
452 public uint nStakeModifierChecksum;
455 /// Chain trust score
457 public uint256 nChainTrust;
463 public enum BlockType
465 BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block
466 BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier
467 BLOCK_STAKE_MODIFIER = (1 << 2), // regenerated stake modifier
471 /// Transaction type.
481 /// Transaction type.
483 public enum OutputType
485 TX_USER = (1 << 0), // User output
486 TX_COINBASE = (1 << 1), // Coinbase output
487 TX_COINSTAKE = (1 << 2), // Coinstake output
488 TX_AVAILABLE = (2 << 0), // Unspent output
489 TX_SPENT = (2 << 1) // Spent output
492 [Table("MerkleNodes")]
493 public class MerkleNode
495 [PrimaryKey, AutoIncrement]
496 public long nMerkleNodeID { get; set; }
499 /// Reference to parent block database item.
501 [ForeignKey(typeof(CBlockStoreItem), Name = "ItemId")]
502 public long nParentBlockID { get; set; }
507 public byte[] TransactionHash { get; set; }
509 public static bool QueryParentBlockCursor(uint256 transactionHash, out CBlockStoreItem cursor)
511 throw new NotImplementedException();
516 public class TxOutItem
519 /// Link the transaction hash with database item identifier.
521 private static ConcurrentDictionary<uint256, long> outMap = new ConcurrentDictionary<uint256, long>();
524 /// Reference to transaction item.
526 [ForeignKey(typeof(MerkleNode), Name = "nMerkleNodeID")]
527 public long nMerkleNodeID { get; set; }
532 public OutputType outputFlags { get; set; }
535 /// Output number in VarInt format.
537 public byte[] OutputNumber { get; set; }
540 /// Output value in VarInt format.
542 public byte[] OutputValue { get; set; }
545 /// Second half of script which contains spending instructions.
547 public byte[] scriptPubKey { get; set; }
550 /// Construct new item from provided transaction data.
552 /// <param name="o"></param>
553 public TxOutItem(CTransaction tx, uint nOut)
555 Contract.Requires<ArgumentException>(nOut < tx.vout.Length);
558 if (!outMap.TryGetValue(tx.Hash, out nMerkleId))
560 // Not in the blockchain
564 OutputNumber = VarInt.EncodeVarInt(nOut);
565 OutputValue = VarInt.EncodeVarInt(tx.vout[nOut].nValue);
566 scriptPubKey = tx.vout[nOut].scriptPubKey;
570 outputFlags |= OutputType.TX_COINBASE;
572 else if (tx.IsCoinStake)
574 outputFlags |= OutputType.TX_COINSTAKE;
579 /// Getter for output number.
584 get { return (uint)VarInt.DecodeVarInt(OutputNumber); }
588 /// Getter for output value.
593 get { return VarInt.DecodeVarInt(OutputValue); }
597 /// Is this a user transaction output?
602 get { return (outputFlags & OutputType.TX_USER) != 0; }
606 /// Is this a coinbase transaction output?
609 public bool IsCoinBase
611 get { return (outputFlags & OutputType.TX_COINBASE) != 0; }
615 /// Is this a coinstake transaction output?
618 public bool IsCoinStake
620 get { return (outputFlags & OutputType.TX_COINSTAKE) != 0; }
624 /// Getter ans setter for IsSpent flag.
629 get { return (outputFlags & OutputType.TX_SPENT) != 0; }
630 set { outputFlags |= value ? OutputType.TX_SPENT : OutputType.TX_AVAILABLE; }
635 [Table("TransactionStorage")]
636 public class CTransactionStoreItem
642 public byte[] TransactionHash { get; set; }
647 [ForeignKey(typeof(CBlockStoreItem), Name = "Hash")]
648 public byte[] BlockHash { get; set; }
651 /// Transaction type flag
653 public TxType txType { get; set; }
656 /// Tx position in file
658 public long nTxPos { get; set; }
663 public int nTxSize { get; set; }
666 /// Serialized output array
668 public byte[] vOut { get; set; }
671 /// Read transaction from file.
673 /// <param name="reader">Stream with read access.</param>
674 /// <param name="tx">CTransaction reference.</param>
675 /// <returns>Result</returns>
676 public bool ReadFromFile(ref Stream reader, out CTransaction tx)
678 var buffer = new byte[CTransaction.nMaxTxSize];
683 reader.Seek(nTxPos, SeekOrigin.Begin); // Seek to transaction offset
685 if (nTxSize != reader.Read(buffer, 0, nTxSize))
690 tx = new CTransaction(buffer);
699 catch (TransactionConstructorException)
707 /// Outputs array access
710 public CTxOut[] Outputs {
711 get { return CTxOut.DeserializeOutputsArray(vOut); }
712 set { vOut = CTxOut.SerializeOutputsArray(value); }
716 public class CBlockStore : IDisposable
718 public const uint nMagicNumber = 0xe5e9e8e4;
720 private bool disposed = false;
721 private object LockObj = new object();
724 /// SQLite connection object.
726 private SQLiteConnection dbConn;
731 private string strBlockFile;
734 /// Index database file.
736 private string strDbFile;
739 /// Map of block tree nodes.
741 private ConcurrentDictionary<uint256, CBlockStoreItem> blockMap = new ConcurrentDictionary<uint256, CBlockStoreItem>();
744 /// Orphaned blocks map.
746 private ConcurrentDictionary<uint256, CBlock> orphanMap = new ConcurrentDictionary<uint256, CBlock>();
747 private ConcurrentDictionary<uint256, CBlock> orphanMapByPrev = new ConcurrentDictionary<uint256, CBlock>();
750 /// Map of unspent items.
752 private ConcurrentDictionary<uint256, CTransactionStoreItem> txMap = new ConcurrentDictionary<uint256, CTransactionStoreItem>();
755 /// Map of the proof-of-stake hashes. This is necessary for stake duplication checks.
757 private ConcurrentDictionary<uint256, uint256> mapProofOfStake = new ConcurrentDictionary<uint256, uint256>();
760 private ConcurrentDictionary<COutPoint, uint> mapStakeSeen = new ConcurrentDictionary<COutPoint, uint>();
761 private ConcurrentDictionary<COutPoint, uint> mapStakeSeenOrphan = new ConcurrentDictionary<COutPoint, uint>();
764 /// Unconfirmed transactions.
766 private ConcurrentDictionary<uint256, CTransaction> mapUnconfirmedTx = new ConcurrentDictionary<uint256, CTransaction>();
769 /// Trust score for the longest chain.
771 private uint256 nBestChainTrust = 0;
774 /// Top block of the best chain.
776 private uint256 nHashBestChain = 0;
779 /// Cursor which is pointing us to the end of best chain.
781 private CBlockStoreItem bestBlockCursor = null;
784 /// Cursor which is always pointing us to genesis block.
786 private CBlockStoreItem genesisBlockCursor = null;
789 /// Current and the only instance of block storage manager. Should be a property with private setter though it's enough for the beginning.
791 public static CBlockStore Instance = null;
794 /// Block file stream with read/write access
796 private Stream fStreamReadWrite;
799 /// Init the block storage manager.
801 /// <param name="IndexDB">Path to index database</param>
802 /// <param name="BlockFile">Path to block file</param>
803 public CBlockStore(string IndexDB = "blockstore.dat", string BlockFile = "blk0001.dat")
806 strBlockFile = BlockFile;
808 bool firstInit = !File.Exists(strDbFile);
809 dbConn = new SQLiteConnection(new SQLitePlatformGeneric(), strDbFile);
811 fStreamReadWrite = File.Open(strBlockFile, FileMode.OpenOrCreate, FileAccess.ReadWrite);
820 dbConn.CreateTable<CBlockStoreItem>(CreateFlags.AutoIncPK);
821 dbConn.CreateTable<CTransactionStoreItem>(CreateFlags.ImplicitPK);
823 var genesisBlock = new CBlock(
825 "01000000" + // nVersion=1
826 "0000000000000000000000000000000000000000000000000000000000000000" + // prevhash is zero
827 "7b0502ad2f9f675528183f83d6385794fbcaa914e6d385c6cb1d866a3b3bb34c" + // merkle root
828 "398e1151" + // nTime=1360105017
829 "ffff0f1e" + // nBits=0x1e0fffff
830 "d3091800" + // nNonce=1575379
832 "01000000" + // nVersion=1
833 "398e1151" + // nTime=1360105017
835 "0000000000000000000000000000000000000000000000000000000000000000" + // input txid is zero
836 "ffffffff" + // n=uint.maxValue
837 "4d" + // scriptSigLen=77
838 "04ffff001d020f274468747470733a2f2f626974636f696e74616c6b2e6f72672f696e6465782e7068703f746f7069633d3133343137392e6d736731353032313936236d736731353032313936" + // scriptSig
839 "ffffffff" + // nSequence=uint.maxValue
841 "0000000000000000" + // nValue=0
842 "00" + // scriptPubkeyLen=0
843 "00000000" + // nLockTime=0
847 // Write block to file.
848 var itemTemplate = new CBlockStoreItem()
853 itemTemplate.FillHeader(genesisBlock.header);
855 if (!AddItemToIndex(ref itemTemplate, ref genesisBlock))
857 throw new Exception("Unable to write genesis block");
863 var blockTreeItems = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] order by [ItemId] asc");
865 // Init list of block items
866 foreach (var item in blockTreeItems)
868 blockMap.TryAdd(item.Hash, item);
870 if (item.IsProofOfStake)
872 // build mapStakeSeen
873 mapStakeSeen.TryAdd(item.prevoutStake, item.nStakeTime);
879 public bool GetTransaction(uint256 TxID, ref CTransaction tx)
881 var reader = new BinaryReader(fStreamReadWrite).BaseStream;
882 var QueryTx = dbConn.Query<CTransactionStoreItem>("select * from [TransactionStorage] where [TransactionHash] = ?", (byte[])TxID);
884 if (QueryTx.Count == 1)
886 return QueryTx[0].ReadFromFile(ref reader, out tx);
894 private bool AddItemToIndex(ref CBlockStoreItem itemTemplate, ref CBlock block)
896 var writer = new BinaryWriter(fStreamReadWrite).BaseStream;
897 uint256 blockHash = itemTemplate.Hash;
899 if (blockMap.ContainsKey(blockHash))
901 // Already have this block.
905 // Compute chain trust score
906 itemTemplate.nChainTrust = (itemTemplate.prev != null ? itemTemplate.prev.nChainTrust : 0) + itemTemplate.nBlockTrust;
908 if (!itemTemplate.SetStakeEntropyBit(Entropy.GetStakeEntropyBit(itemTemplate.nHeight, blockHash)))
910 return false; // SetStakeEntropyBit() failed
913 // Save proof-of-stake hash value
914 if (itemTemplate.IsProofOfStake)
916 uint256 hashProofOfStake;
917 if (!GetProofOfStakeHash(blockHash, out hashProofOfStake))
919 return false; // hashProofOfStake not found
921 itemTemplate.hashProofOfStake = hashProofOfStake;
924 // compute stake modifier
925 long nStakeModifier = 0;
926 bool fGeneratedStakeModifier = false;
927 if (!StakeModifier.ComputeNextStakeModifier(itemTemplate, ref nStakeModifier, ref fGeneratedStakeModifier))
929 return false; // ComputeNextStakeModifier() failed
932 itemTemplate.SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
933 itemTemplate.nStakeModifierChecksum = StakeModifier.GetStakeModifierChecksum(itemTemplate);
935 // TODO: verify stake modifier checkpoints
938 if (block.IsProofOfStake)
940 itemTemplate.SetProofOfStake();
942 itemTemplate.prevoutStake = block.vtx[1].vin[0].prevout;
943 itemTemplate.nStakeTime = block.vtx[1].nTime;
946 if (!itemTemplate.WriteToFile(ref writer, ref block))
951 if (dbConn.Insert(itemTemplate) == 0 || !blockMap.TryAdd(blockHash, itemTemplate))
956 if (itemTemplate.nChainTrust > nBestChainTrust)
960 // TODO: SetBestChain implementation
963 if (!SetBestChain(ref itemTemplate))
965 return false; // SetBestChain failed.
970 // We have no SetBestChain and ConnectBlock/Disconnect block yet, so adding these transactions manually.
971 for (int i = 0; i < block.vtx.Length; i++)
973 // Handle trasactions using our temporary stub algo
975 if (!block.vtx[i].VerifyScripts())
980 var nTxOffset = itemTemplate.nBlockPos + block.GetTxOffset(i);
981 TxType txnType = TxType.TX_USER;
983 if (block.vtx[i].IsCoinBase)
985 txnType = TxType.TX_COINBASE;
987 else if (block.vtx[i].IsCoinStake)
989 txnType = TxType.TX_COINSTAKE;
992 var NewTxItem = new CTransactionStoreItem()
994 TransactionHash = block.vtx[i].Hash,
995 BlockHash = blockHash,
997 nTxSize = block.vtx[i].Size,
1001 dbConn.Insert(NewTxItem);
1007 private bool SetBestChain(ref CBlockStoreItem cursor)
1009 dbConn.BeginTransaction();
1011 uint256 hashBlock = cursor.Hash;
1013 if (genesisBlockCursor == null && hashBlock == NetUtils.nHashGenesisBlock)
1015 genesisBlockCursor = cursor;
1017 else if (nHashBestChain == (uint256)cursor.prevHash)
1019 if (!SetBestChainInner(cursor))
1026 // the first block in the new chain that will cause it to become the new best chain
1027 CBlockStoreItem cursorIntermediate = cursor;
1029 // list of blocks that need to be connected afterwards
1030 List<CBlockStoreItem> secondary = new List<CBlockStoreItem>();
1032 // Reorganize is costly in terms of db load, as it works in a single db transaction.
1033 // Try to limit how much needs to be done inside
1034 while (cursorIntermediate.prev != null && cursorIntermediate.prev.nChainTrust > bestBlockCursor.nChainTrust)
1036 secondary.Add(cursorIntermediate);
1037 cursorIntermediate = cursorIntermediate.prev;
1040 // Switch to new best branch
1041 if (!Reorganize(cursorIntermediate))
1044 InvalidChainFound(cursor);
1045 return false; // reorganize failed
1052 throw new NotImplementedException();
1055 private void InvalidChainFound(CBlockStoreItem cursor)
1057 throw new NotImplementedException();
1060 private bool Reorganize(CBlockStoreItem cursorIntermediate)
1062 throw new NotImplementedException();
1065 private bool SetBestChainInner(CBlockStoreItem cursor)
1067 uint256 hash = cursor.Hash;
1070 // Adding to current best branch
1071 if (!ConnectBlock(cursor, false, out block) || !WriteHashBestChain(hash))
1074 InvalidChainFound(cursor);
1078 // Add to current best branch
1079 cursor.prev.next = cursor;
1083 // Delete redundant memory transactions
1084 foreach (var tx in block.vtx)
1087 mapUnconfirmedTx.TryRemove(tx.Hash, out dummy);
1093 private bool ConnectBlock(CBlockStoreItem cursor, bool fJustCheck, out CBlock block)
1095 var reader = new BinaryReader(fStreamReadWrite).BaseStream;
1096 if (cursor.ReadFromFile(ref reader, out block))
1098 return false; // Unable to read block from file.
1101 // Check it again in case a previous version let a bad block in, but skip BlockSig checking
1102 if (!block.CheckBlock(!fJustCheck, !fJustCheck, false))
1104 return false; // Invalid block found.
1107 // TODO: the remaining stuff lol :D
1109 throw new NotImplementedException();
1112 private bool WriteHashBestChain(uint256 hash)
1114 throw new NotImplementedException();
1118 /// Try to find proof-of-stake hash in the map.
1120 /// <param name="blockHash">Block hash</param>
1121 /// <param name="hashProofOfStake">Proof-of-stake hash</param>
1122 /// <returns>Proof-of-Stake hash value</returns>
1123 private bool GetProofOfStakeHash(uint256 blockHash, out uint256 hashProofOfStake)
1125 return mapProofOfStake.TryGetValue(blockHash, out hashProofOfStake);
1128 public bool AcceptBlock(ref CBlock block)
1130 uint256 nHash = block.header.Hash;
1132 if (blockMap.ContainsKey(nHash))
1134 // Already have this block.
1138 CBlockStoreItem prevBlockCursor = null;
1139 if (!blockMap.TryGetValue(block.header.prevHash, out prevBlockCursor))
1141 // Unable to get the cursor.
1145 var prevBlockHeader = prevBlockCursor.BlockHeader;
1147 // TODO: proof-of-work/proof-of-stake verification
1148 uint nHeight = prevBlockCursor.nHeight + 1;
1150 // Check timestamp against prev
1151 if (NetUtils.FutureDrift(block.header.nTime) < prevBlockHeader.nTime)
1153 // block's timestamp is too early
1157 // Check that all transactions are finalized
1158 foreach (var tx in block.vtx)
1160 if (!tx.IsFinal(nHeight, block.header.nTime))
1166 // TODO: Enforce rule that the coinbase starts with serialized block height
1168 // Write block to file.
1169 var itemTemplate = new CBlockStoreItem()
1174 itemTemplate.FillHeader(block.header);
1176 if (!AddItemToIndex(ref itemTemplate, ref block))
1184 public bool GetBlock(uint256 blockHash, ref CBlock block, ref long nBlockPos)
1186 var reader = new BinaryReader(fStreamReadWrite).BaseStream;
1188 var QueryBlock = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] where [Hash] = ?", (byte[])blockHash);
1190 if (QueryBlock.Count == 1)
1192 nBlockPos = QueryBlock[0].nBlockPos;
1193 return QueryBlock[0].ReadFromFile(ref reader, out block);
1201 public bool GetBlockByTransactionID(uint256 TxID, ref CBlock block, ref CTransaction tx, ref long nBlockPos, ref long nTxPos)
1203 var QueryTx = dbConn.Query<CTransactionStoreItem>("select * from [TransactionStorage] where [TransactionHash] = ?", (byte[])TxID);
1205 if (QueryTx.Count == 1)
1207 nTxPos = QueryTx[0].nTxPos;
1208 return GetBlock(QueryTx[0].BlockHash, ref block, ref nBlockPos);
1217 /// Get block cursor from map.
1219 /// <param name="blockHash">block hash</param>
1220 /// <returns>Cursor or null</returns>
1221 public CBlockStoreItem GetCursor(uint256 blockHash)
1225 // Genesis block has zero prevHash and no parent.
1229 // First, check our block map.
1230 CBlockStoreItem item = null;
1231 if (blockMap.TryGetValue(blockHash, out item))
1236 // Trying to get cursor from the database.
1237 var QueryBlockCursor = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] where [Hash] = ?", (byte[])blockHash);
1239 if (QueryBlockCursor.Count == 1)
1241 blockMap.TryAdd(blockHash, QueryBlockCursor[0]);
1243 return QueryBlockCursor[0];
1251 /// Update cursor in memory and on disk.
1253 /// <param name="originalItem">Original cursor</param>
1254 /// <param name="newItem">New cursor</param>
1255 /// <returns></returns>
1256 public bool UpdateCursor(CBlockStoreItem originalItem, ref CBlockStoreItem newItem)
1258 if (blockMap.TryUpdate(originalItem.Hash, newItem, originalItem))
1260 return dbConn.Update(newItem) != 0;
1266 public bool ProcessBlock(ref CBlock block)
1268 var blockHash = block.header.Hash;
1270 if (blockMap.ContainsKey(blockHash))
1272 // We already have this block.
1276 if (orphanMap.ContainsKey(blockHash))
1278 // We already have block in the list of orphans.
1282 // TODO: Limited duplicity on stake and reserialization of block signature
1284 if (!block.CheckBlock(true, true, true))
1286 // Preliminary checks failure.
1290 if (block.IsProofOfStake)
1292 if (!block.SignatureOK)
1294 // Proof-of-Stake signature validation failure.
1298 // TODO: proof-of-stake validation
1300 uint256 hashProofOfStake = 0, targetProofOfStake = 0;
1301 if (!StakeModifier.CheckProofOfStake(block.vtx[1], block.header.nBits, ref hashProofOfStake, ref targetProofOfStake))
1303 return false; // do not error here as we expect this during initial block download
1305 if (!mapProofOfStake.ContainsKey(blockHash))
1307 // add to mapProofOfStake
1308 mapProofOfStake.TryAdd(blockHash, hashProofOfStake);
1313 // TODO: difficulty verification
1315 // If don't already have its previous block, shunt it off to holding area until we get it
1316 if (!blockMap.ContainsKey(block.header.prevHash))
1318 if (block.IsProofOfStake)
1320 // TODO: limit duplicity on stake
1323 var block2 = new CBlock(block);
1324 orphanMap.TryAdd(blockHash, block2);
1325 orphanMapByPrev.TryAdd(blockHash, block2);
1330 // Store block to disk
1331 if (!AcceptBlock(ref block))
1337 // Recursively process any orphan blocks that depended on this one
1338 var orphansQueue = new List<uint256>();
1339 orphansQueue.Add(blockHash);
1341 for (int i = 0; i < orphansQueue.Count; i++)
1343 var hashPrev = orphansQueue[i];
1345 foreach (var pair in orphanMap)
1347 var orphanBlock = pair.Value;
1349 if (orphanBlock.header.prevHash == blockHash)
1351 if (AcceptBlock(ref orphanBlock))
1353 orphansQueue.Add(pair.Key);
1357 orphanMap.TryRemove(pair.Key, out dummy1);
1362 orphanMap.TryRemove(hashPrev, out dummy2);
1368 public bool ParseBlockFile(string BlockFile = "bootstrap.dat")
1370 // TODO: Rewrite completely.
1374 var buffer = new byte[CBlock.nMaxBlockSize]; // Max block size is 1Mb
1375 var intBuffer = new byte[4];
1377 var fStream2 = File.OpenRead(BlockFile);
1378 var readerForBlocks = new BinaryReader(fStream2).BaseStream;
1380 readerForBlocks.Seek(nOffset, SeekOrigin.Begin); // Seek to previous offset + previous block length
1382 dbConn.BeginTransaction();
1384 while (readerForBlocks.Read(buffer, 0, 4) == 4) // Read magic number
1386 var nMagic = BitConverter.ToUInt32(buffer, 0);
1387 if (nMagic != 0xe5e9e8e4)
1389 throw new Exception("Incorrect magic number.");
1392 var nBytesRead = readerForBlocks.Read(buffer, 0, 4);
1393 if (nBytesRead != 4)
1395 throw new Exception("BLKSZ EOF");
1398 var nBlockSize = BitConverter.ToInt32(buffer, 0);
1400 nOffset = readerForBlocks.Position;
1402 nBytesRead = readerForBlocks.Read(buffer, 0, nBlockSize);
1404 if (nBytesRead == 0 || nBytesRead != nBlockSize)
1406 throw new Exception("BLK EOF");
1409 var block = new CBlock(buffer);
1410 var hash = block.header.Hash;
1412 if (blockMap.ContainsKey(hash))
1417 if (!ProcessBlock(ref block))
1419 throw new Exception("Invalid block: " + block.header.Hash);
1422 int nCount = blockMap.Count;
1423 Console.WriteLine("nCount={0}, Hash={1}, Time={2}", nCount, block.header.Hash, DateTime.Now); // Commit on each 100th block
1425 if (nCount % 100 == 0 && nCount != 0)
1427 Console.WriteLine("Commit...");
1429 dbConn.BeginTransaction();
1443 public void Dispose()
1446 GC.SuppressFinalize(this);
1449 protected virtual void Dispose(bool disposing)
1455 // Free other state (managed objects).
1457 fStreamReadWrite.Dispose();