1 \feffusing SQLite.Net.Attributes;
2 using SQLiteNetExtensions.Attributes;
9 public class ChainState
11 [PrimaryKey, AutoIncrement]
12 public long itemId { get; set; }
15 /// Hash of top block in the best chain
17 public byte[] HashBestChain { get; set; }
20 /// Total trust score of best chain
22 public byte[] BestChainTrust { get; set; }
24 public uint nBestHeight { get; set; }
27 public uint256 nBestChainTrust
29 get { return BestChainTrust; }
30 set { BestChainTrust = value; }
34 public uint256 nHashBestChain
36 get { return HashBestChain; }
37 set { HashBestChain = value; }
41 [Table("BlockStorage")]
42 public class CBlockStoreItem
44 #region IBlockStorageItem
46 /// Item ID in the database
48 [PrimaryKey, AutoIncrement]
49 public long ItemID { get; set; }
52 /// PBKDF2+Salsa20 of block hash
55 public byte[] Hash { get; set; }
58 /// Version of block schema
61 public uint nVersion { get; set; }
64 /// Previous block hash.
67 public byte[] prevHash { get; set; }
72 [Column("merkleRoot")]
73 public byte[] merkleRoot { get; set; }
79 public uint nTime { get; set; }
82 /// Compressed difficulty representation.
85 public uint nBits { get; set; }
91 public uint nNonce { get; set; }
97 public byte[] nextHash { get; set; }
102 [Column("BlockTypeFlag")]
103 public BlockType BlockTypeFlag { get; set; }
108 [Column("nStakeModifier")]
109 public long nStakeModifier { get; set; }
112 /// Proof-of-Stake hash
114 [Column("hashProofOfStake")]
115 public byte[] hashProofOfStake { get; set; }
118 /// Stake generation outpoint.
120 [Column("prevoutStake")]
121 public byte[] prevoutStake { get; set; }
124 /// Stake generation time.
126 [Column("nStakeTime")]
127 public uint nStakeTime { get; set; }
130 /// Block height, encoded in VarInt format
133 public uint nHeight { get; set; }
136 /// Chain trust score, serialized and trimmed uint256 representation.
138 [Column("ChainTrust")]
139 public byte[] ChainTrust { get; set; }
142 /// Block position in file, encoded in VarInt format
145 public byte[] BlockPos { get; set; }
148 /// Block size in bytes, encoded in VarInt format
150 [Column("BlockSize")]
151 public byte[] BlockSize { get; set; }
155 /// Accessor and mutator for BlockPos value.
158 public long nBlockPos
160 get { return (long)VarInt.DecodeVarInt(BlockPos); }
161 set { BlockPos = VarInt.EncodeVarInt(value); }
165 /// Accessor and mutator for BlockSize value.
168 public int nBlockSize
170 get { return (int)VarInt.DecodeVarInt(BlockSize); }
171 set { BlockSize = VarInt.EncodeVarInt(value); }
175 /// Fill database item with data from given block header.
177 /// <param name="header">Block header</param>
178 /// <returns>Header hash</returns>
179 public uint256 FillHeader(CBlockHeader header)
181 uint256 _hash = header.Hash;
185 nVersion = header.nVersion;
186 prevHash = header.prevHash;
187 merkleRoot = header.merkleRoot;
188 nTime = header.nTime;
189 nBits = header.nBits;
190 nNonce = header.nNonce;
196 /// Reconstruct block header from item data.
198 public CBlockHeader BlockHeader
202 CBlockHeader header = new CBlockHeader();
204 header.nVersion = nVersion;
205 header.prevHash = prevHash;
206 header.merkleRoot = merkleRoot;
207 header.nTime = nTime;
208 header.nBits = nBits;
209 header.nNonce = nNonce;
216 /// Read block from file.
218 /// <param name="reader">Stream with read access.</param>
219 /// <param name="reader">CBlock reference.</param>
220 /// <returns>Result</returns>
221 public bool ReadFromFile(ref Stream reader, out CBlock block)
223 var buffer = new byte[nBlockSize];
228 reader.Seek(nBlockPos, SeekOrigin.Begin);
230 if (nBlockSize != reader.Read(buffer, 0, nBlockSize))
235 block = new CBlock(buffer);
244 catch (BlockException)
246 // Constructor exception
252 /// Writes given block to file and prepares cursor object for insertion into the database.
254 /// <param name="writer">Stream with write access.</param>
255 /// <param name="block">CBlock reference.</param>
256 /// <returns>Result</returns>
257 public bool WriteToFile(ref Stream writer, ref CBlock block)
261 byte[] blockBytes = block;
263 var magicBytes = BitConverter.GetBytes(CBlockStore.nMagicNumber);
264 var blkLenBytes = BitConverter.GetBytes(blockBytes.Length);
266 // Seek to the end and then append magic bytes there.
267 writer.Seek(0, SeekOrigin.End);
268 writer.Write(magicBytes, 0, magicBytes.Length);
269 writer.Write(blkLenBytes, 0, blkLenBytes.Length);
271 // Save block size and current position in the block cursor fields.
272 nBlockPos = writer.Position;
273 nBlockSize = blockBytes.Length;
275 // Write block and flush the stream.
276 writer.Write(blockBytes, 0, blockBytes.Length);
288 // Some serialization error
294 /// Previous block cursor
297 public CBlockStoreItem prev
299 get { return CBlockStore.Instance.GetMapCursor(prevHash); }
303 /// Next block cursor
306 public CBlockStoreItem next
310 if (nextHash == null)
315 return CBlockStore.Instance.GetMapCursor(nextHash);
319 nextHash = value.Hash;
321 CBlockStore.Instance.UpdateMapCursor(this);
328 get { return (next != null); }
332 /// STake modifier generation flag
335 public bool GeneratedStakeModifier
337 get { return (BlockTypeFlag & BlockType.BLOCK_STAKE_MODIFIER) != 0; }
341 /// Stake entropy bit
344 public uint StakeEntropyBit
346 get { return ((uint)(BlockTypeFlag & BlockType.BLOCK_STAKE_ENTROPY) >> 1); }
350 /// Sets stake modifier and flag.
352 /// <param name="nModifier">New stake modifier.</param>
353 /// <param name="fGeneratedStakeModifier">Set generation flag?</param>
354 public void SetStakeModifier(long nModifier, bool fGeneratedStakeModifier)
356 nStakeModifier = nModifier;
357 if (fGeneratedStakeModifier)
358 BlockTypeFlag |= BlockType.BLOCK_STAKE_MODIFIER;
364 /// <param name="nEntropyBit">Entropy bit value (0 or 1).</param>
365 /// <returns>False if value is our of range.</returns>
366 public bool SetStakeEntropyBit(byte nEntropyBit)
370 BlockTypeFlag |= (nEntropyBit != 0 ? BlockType.BLOCK_STAKE_ENTROPY : 0);
375 /// Set proof-of-stake flag.
377 public void SetProofOfStake()
379 BlockTypeFlag |= BlockType.BLOCK_PROOF_OF_STAKE;
383 /// Block has no proof-of-stake flag.
386 public bool IsProofOfWork
388 get { return (BlockTypeFlag & BlockType.BLOCK_PROOF_OF_STAKE) == 0; }
392 /// Block has proof-of-stake flag set.
395 public bool IsProofOfStake
397 get { return (BlockTypeFlag & BlockType.BLOCK_PROOF_OF_STAKE) != 0; }
401 /// Block trust score.
404 public uint256 nBlockTrust
409 nTarget.Compact = nBits;
412 if (nTime < NetInfo.nChainChecksSwitchTime)
414 return IsProofOfStake ? (new uint256(1) << 256) / (nTarget + 1) : 1;
419 // Calculate work amount for block
420 var nPoWTrust = NetInfo.nPoWBase / (nTarget + 1);
422 // Set nPowTrust to 1 if we are checking PoS block or PoW difficulty is too low
423 nPoWTrust = (IsProofOfStake || !nPoWTrust) ? 1 : nPoWTrust;
425 // Return nPoWTrust for the first 12 blocks
426 if (prev == null || prev.nHeight < 12)
429 CBlockStoreItem currentIndex = prev;
433 var nNewTrust = (new uint256(1) << 256) / (nTarget + 1);
435 // Return 1/3 of score if parent block is not the PoW block
436 if (!prev.IsProofOfWork)
438 return nNewTrust / 3;
443 // Check last 12 blocks type
444 while (prev.nHeight - currentIndex.nHeight < 12)
446 if (currentIndex.IsProofOfWork)
450 currentIndex = currentIndex.prev;
453 // Return 1/3 of score if less than 3 PoW blocks found
456 return nNewTrust / 3;
463 var nLastBlockTrust = prev.nChainTrust - prev.prev.nChainTrust;
465 // Return nPoWTrust + 2/3 of previous block score if two parent blocks are not PoS blocks
466 if (!prev.IsProofOfStake || !prev.prev.IsProofOfStake)
468 return nPoWTrust + (2 * nLastBlockTrust / 3);
473 // Check last 12 blocks type
474 while (prev.nHeight - currentIndex.nHeight < 12)
476 if (currentIndex.IsProofOfStake)
480 currentIndex = currentIndex.prev;
483 // Return nPoWTrust + 2/3 of previous block score if less than 7 PoS blocks found
486 return nPoWTrust + (2 * nLastBlockTrust / 3);
489 nTarget.Compact = prev.nBits;
496 var nNewTrust = (new uint256(1) << 256) / (nTarget + 1);
498 // Return nPoWTrust + full trust score for previous block nBits
499 return nPoWTrust + nNewTrust;
505 /// Stake modifier checksum.
507 public uint nStakeModifierChecksum;
510 /// Chain trust score
513 public uint256 nChainTrust
515 get { return Interop.AppendWithZeros(ChainTrust); }
516 set { ChainTrust = Interop.TrimArray(value); }
519 public long nMint { get; internal set; }
520 public long nMoneySupply { get; internal set; }
526 public enum BlockType
528 BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block
529 BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier
530 BLOCK_STAKE_MODIFIER = (1 << 2), // regenerated stake modifier
534 /// Transaction type.
536 public enum TxFlags : byte
546 public enum OutputFlags : byte
548 AVAILABLE, // Unspent output
549 SPENT // Spent output
552 [Table("MerkleNodes")]
553 public class CMerkleNode
559 [PrimaryKey, AutoIncrement]
560 public long nMerkleNodeID { get; set; }
563 /// Reference to parent block database item.
565 [ForeignKey(typeof(CBlockStoreItem), Name = "ItemId")]
566 public long nParentBlockID { get; set; }
569 /// Transaction timestamp
572 public uint nTime { get; set; }
575 /// Transaction type flag
577 [Column("TransactionFlags")]
578 public TxFlags TransactionFlags { get; set; }
583 [Column("TransactionHash")]
584 public byte[] TransactionHash { get; set; }
587 /// Transaction offset from the beginning of block header, encoded in VarInt format.
590 public byte[] TxOffset { get; set; }
593 /// Transaction size, encoded in VarInt format.
596 public byte[] TxSize { get; set; }
600 /// Read transaction from file.
602 /// <param name="reader">Stream with read access.</param>
603 /// <param name="tx">CTransaction reference.</param>
604 /// <returns>Result</returns>
605 public bool ReadFromFile(ref Stream reader, long nBlockPos, out CTransaction tx)
607 var buffer = new byte[CTransaction.nMaxTxSize];
613 reader.Seek(nBlockPos + nTxOffset, SeekOrigin.Begin); // Seek to transaction offset
615 if (nTxSize != reader.Read(buffer, 0, (int)nTxSize))
620 tx = new CTransaction(buffer);
629 catch (TransactionConstructorException)
637 /// Transaction offset accessor
640 public long nTxOffset
642 get { return (long)VarInt.DecodeVarInt(TxOffset); }
643 private set { TxOffset = VarInt.EncodeVarInt(value); }
647 /// Transaction size accessor
652 get { return (uint)VarInt.DecodeVarInt(TxSize); }
653 private set { TxSize = VarInt.EncodeVarInt(value); }
657 public bool IsCoinBase
659 get { return TransactionFlags == TxFlags.TX_COINBASE; }
663 public bool IsCoinStake
665 get { return TransactionFlags == TxFlags.TX_COINSTAKE; }
672 public CMerkleNode(CTransaction tx)
680 TransactionHash = tx.Hash;
684 TransactionFlags = TxFlags.TX_COINBASE;
686 else if (tx.IsCoinStake)
688 TransactionFlags = TxFlags.TX_COINSTAKE;
692 TransactionFlags = TxFlags.TX_USER;
696 public CMerkleNode(long nBlockId, long nOffset, CTransaction tx)
700 nParentBlockID = nBlockId;
704 TransactionHash = tx.Hash;
708 TransactionFlags |= TxFlags.TX_COINBASE;
710 else if (tx.IsCoinStake)
712 TransactionFlags |= TxFlags.TX_COINSTAKE;
716 TransactionFlags |= TxFlags.TX_USER;
723 public class TxOutItem
726 /// Outpoint identifier.
728 [PrimaryKey, AutoIncrement]
729 public long nOutpointID { get; set; }
732 /// Reference to transaction item.
734 [ForeignKey(typeof(CMerkleNode), Name = "nMerkleNodeID")]
735 public long nMerkleNodeID { get; set; }
740 public OutputFlags outputFlags { get; set; }
743 /// Output number in VarInt format.
745 public byte[] OutputNumber { get; set; }
748 /// Output value in VarInt format.
750 public byte[] OutputValue { get; set; }
753 /// Second half of script which contains spending instructions.
755 public byte[] scriptPubKey { get; set; }
758 /// Getter for output number.
763 get { return (uint)VarInt.DecodeVarInt(OutputNumber); }
764 set { OutputNumber = VarInt.EncodeVarInt(value); }
768 /// Getter for output value.
773 get { return VarInt.DecodeVarInt(OutputValue); }
774 set { OutputValue = VarInt.EncodeVarInt(value); }
778 /// Getter ans setter for IsSpent flag.
783 get { return (outputFlags & OutputFlags.SPENT) != 0; }
784 set { outputFlags |= value ? OutputFlags.SPENT : OutputFlags.AVAILABLE; }
790 /// TxOut + transaction hash
792 public class InputsJoin : TxOutItem
794 public byte[] TransactionHash { get; set; }
797 /// To avoid awkwardness of sqlite wrapper.
799 /// <returns></returns>
800 public TxOutItem getTxOutItem()
802 return new TxOutItem()
804 nOutpointID = nOutpointID,
805 nMerkleNodeID = nMerkleNodeID,
806 outputFlags = outputFlags,
807 OutputNumber = OutputNumber,
808 OutputValue = OutputValue,
809 scriptPubKey = scriptPubKey