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 /// Chain trust score
508 public uint256 nChainTrust
510 get { return Interop.AppendWithZeros(ChainTrust); }
511 set { ChainTrust = Interop.TrimArray(value); }
514 public long nMint { get; internal set; }
515 public long nMoneySupply { get; internal set; }
521 public enum BlockType
523 BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block
524 BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier
525 BLOCK_STAKE_MODIFIER = (1 << 2), // regenerated stake modifier
529 /// Transaction type.
531 public enum TxFlags : byte
541 public enum OutputFlags : byte
543 AVAILABLE, // Unspent output
544 SPENT // Spent output
547 [Table("MerkleNodes")]
548 public class CMerkleNode
554 [PrimaryKey, AutoIncrement]
555 public long nMerkleNodeID { get; set; }
558 /// Reference to parent block database item.
560 [ForeignKey(typeof(CBlockStoreItem), Name = "ItemId")]
561 public long nParentBlockID { get; set; }
564 /// Transaction timestamp
567 public uint nTime { get; set; }
570 /// Transaction type flag
572 [Column("TransactionFlags")]
573 public TxFlags TransactionFlags { get; set; }
578 [Column("TransactionHash")]
579 public byte[] TransactionHash { get; set; }
582 /// Transaction offset from the beginning of block header, encoded in VarInt format.
585 public byte[] TxOffset { get; set; }
588 /// Transaction size, encoded in VarInt format.
591 public byte[] TxSize { get; set; }
595 /// Read transaction from file.
597 /// <param name="reader">Stream with read access.</param>
598 /// <param name="tx">CTransaction reference.</param>
599 /// <returns>Result</returns>
600 public bool ReadFromFile(ref Stream reader, long nBlockPos, out CTransaction tx)
602 var buffer = new byte[CTransaction.nMaxTxSize];
608 reader.Seek(nBlockPos + nTxOffset, SeekOrigin.Begin); // Seek to transaction offset
610 if (nTxSize != reader.Read(buffer, 0, (int)nTxSize))
615 tx = new CTransaction(buffer);
624 catch (TransactionConstructorException)
632 /// Transaction offset accessor
635 public long nTxOffset
637 get { return (long)VarInt.DecodeVarInt(TxOffset); }
638 private set { TxOffset = VarInt.EncodeVarInt(value); }
642 /// Transaction size accessor
647 get { return (uint)VarInt.DecodeVarInt(TxSize); }
648 private set { TxSize = VarInt.EncodeVarInt(value); }
652 public bool IsCoinBase
654 get { return TransactionFlags == TxFlags.TX_COINBASE; }
658 public bool IsCoinStake
660 get { return TransactionFlags == TxFlags.TX_COINSTAKE; }
667 public CMerkleNode(CTransaction tx)
675 TransactionHash = tx.Hash;
679 TransactionFlags = TxFlags.TX_COINBASE;
681 else if (tx.IsCoinStake)
683 TransactionFlags = TxFlags.TX_COINSTAKE;
687 TransactionFlags = TxFlags.TX_USER;
691 public CMerkleNode(long nBlockId, long nOffset, CTransaction tx)
695 nParentBlockID = nBlockId;
699 TransactionHash = tx.Hash;
703 TransactionFlags |= TxFlags.TX_COINBASE;
705 else if (tx.IsCoinStake)
707 TransactionFlags |= TxFlags.TX_COINSTAKE;
711 TransactionFlags |= TxFlags.TX_USER;
718 public class TxOutItem
721 /// Outpoint identifier.
723 [PrimaryKey, AutoIncrement]
724 public long nOutpointID { get; set; }
727 /// Reference to transaction item.
729 [ForeignKey(typeof(CMerkleNode), Name = "nMerkleNodeID")]
730 public long nMerkleNodeID { get; set; }
735 public OutputFlags outputFlags { get; set; }
738 /// Output number in VarInt format.
740 public byte[] OutputNumber { get; set; }
743 /// Output value in VarInt format.
745 public byte[] OutputValue { get; set; }
748 /// Second half of script which contains spending instructions.
750 public byte[] scriptPubKey { get; set; }
753 /// Getter for output number.
758 get { return (uint)VarInt.DecodeVarInt(OutputNumber); }
759 set { OutputNumber = VarInt.EncodeVarInt(value); }
763 /// Getter for output value.
768 get { return VarInt.DecodeVarInt(OutputValue); }
769 set { OutputValue = VarInt.EncodeVarInt(value); }
773 /// Getter ans setter for IsSpent flag.
778 get { return (outputFlags & OutputFlags.SPENT) != 0; }
779 set { outputFlags |= value ? OutputFlags.SPENT : OutputFlags.AVAILABLE; }
785 /// TxOut + transaction hash
787 public class InputsJoin : TxOutItem
789 public byte[] TransactionHash { get; set; }
792 /// To avoid awkwardness of sqlite wrapper.
794 /// <returns></returns>
795 public TxOutItem getTxOutItem()
797 return new TxOutItem()
799 nOutpointID = nOutpointID,
800 nMerkleNodeID = nMerkleNodeID,
801 outputFlags = outputFlags,
802 OutputNumber = OutputNumber,
803 OutputValue = OutputValue,
804 scriptPubKey = scriptPubKey