nBlockTrust cleanup.
[NovacoinLibrary.git] / Novacoin / DatabaseObjects.cs
1 \feffusing SQLite.Net.Attributes;
2 using SQLiteNetExtensions.Attributes;
3 using System;
4 using System.IO;
5
6 namespace Novacoin
7 {
8     [Table("ChainState")]
9     public class ChainState
10     {
11         [PrimaryKey, AutoIncrement]
12         public long itemId { get; set; }
13
14         /// <summary>
15         /// Hash of top block in the best chain
16         /// </summary>
17         public byte[] HashBestChain { get; set; }
18
19         /// <summary>
20         /// Total trust score of best chain
21         /// </summary>
22         public byte[] BestChainTrust { get; set; }
23
24         public uint nBestHeight { get; set; }
25
26         [Ignore]
27         public uint256 nBestChainTrust
28         {
29             get { return BestChainTrust; }
30             set { BestChainTrust = value; }
31         }
32
33         [Ignore]
34         public uint256 nHashBestChain
35         {
36             get { return HashBestChain; }
37             set { HashBestChain = value; }
38         }
39     }
40
41     [Table("BlockStorage")]
42     public class CBlockStoreItem
43     {
44         #region IBlockStorageItem
45         /// <summary>
46         /// Item ID in the database
47         /// </summary>
48         [PrimaryKey, AutoIncrement]
49         public long ItemID { get; set; }
50
51         /// <summary>
52         /// PBKDF2+Salsa20 of block hash
53         /// </summary>
54         [Unique]
55         public byte[] Hash { get; set; }
56
57         /// <summary>
58         /// Version of block schema
59         /// </summary>
60         [Column("nVersion")]
61         public uint nVersion { get; set; }
62
63         /// <summary>
64         /// Previous block hash.
65         /// </summary>
66         [Column("prevHash")]
67         public byte[] prevHash { get; set; }
68
69         /// <summary>
70         /// Merkle root hash.
71         /// </summary>
72         [Column("merkleRoot")]
73         public byte[] merkleRoot { get; set; }
74
75         /// <summary>
76         /// Block timestamp.
77         /// </summary>
78         [Column("nTime")]
79         public uint nTime { get; set; }
80
81         /// <summary>
82         /// Compressed difficulty representation.
83         /// </summary>
84         [Column("nBits")]
85         public uint nBits { get; set; }
86
87         /// <summary>
88         /// Nonce counter.
89         /// </summary>
90         [Column("nNonce")]
91         public uint nNonce { get; set; }
92
93         /// <summary>
94         /// Next block hash.
95         /// </summary>
96         [Column("nextHash")]
97         public byte[] nextHash { get; set; }
98
99         /// <summary>
100         /// Block type flags
101         /// </summary>
102         [Column("BlockTypeFlag")]
103         public BlockType BlockTypeFlag { get; set; }
104
105         /// <summary>
106         /// Stake modifier
107         /// </summary>
108         [Column("nStakeModifier")]
109         public long nStakeModifier { get; set; }
110
111         /// <summary>
112         /// Proof-of-Stake hash
113         /// </summary>
114         [Column("hashProofOfStake")]
115         public byte[] hashProofOfStake { get; set; }
116
117         /// <summary>
118         /// Stake generation outpoint.
119         /// </summary>
120         [Column("prevoutStake")]
121         public byte[] prevoutStake { get; set; }
122
123         /// <summary>
124         /// Stake generation time.
125         /// </summary>
126         [Column("nStakeTime")]
127         public uint nStakeTime { get; set; }
128
129         /// <summary>
130         /// Block height, encoded in VarInt format
131         /// </summary>
132         [Column("nHeight")]
133         public uint nHeight { get; set; }
134
135         /// <summary>
136         /// Chain trust score, serialized and trimmed uint256 representation.
137         /// </summary>
138         [Column("ChainTrust")]
139         public byte[] ChainTrust { get; set; }
140
141         /// <summary>
142         /// Block position in file, encoded in VarInt format
143         /// </summary>
144         [Column("BlockPos")]
145         public byte[] BlockPos { get; set; }
146
147         /// <summary>
148         /// Block size in bytes, encoded in VarInt format
149         /// </summary>
150         [Column("BlockSize")]
151         public byte[] BlockSize { get; set; }
152         #endregion
153
154         /// <summary>
155         /// Accessor and mutator for BlockPos value.
156         /// </summary>
157         [Ignore]
158         public long nBlockPos
159         {
160             get { return (long)VarInt.DecodeVarInt(BlockPos); }
161             set { BlockPos = VarInt.EncodeVarInt(value); }
162         }
163
164         /// <summary>
165         /// Accessor and mutator for BlockSize value.
166         /// </summary>
167         [Ignore]
168         public int nBlockSize
169         {
170             get { return (int)VarInt.DecodeVarInt(BlockSize); }
171             set { BlockSize = VarInt.EncodeVarInt(value); }
172         }
173
174         /// <summary>
175         /// Fill database item with data from given block header.
176         /// </summary>
177         /// <param name="header">Block header</param>
178         /// <returns>Header hash</returns>
179         public uint256 FillHeader(CBlockHeader header)
180         {
181             uint256 _hash = header.Hash;
182
183             Hash = _hash;
184
185             nVersion = header.nVersion;
186             prevHash = header.prevHash;
187             merkleRoot = header.merkleRoot;
188             nTime = header.nTime;
189             nBits = header.nBits;
190             nNonce = header.nNonce;
191
192             return _hash;
193         }
194
195         /// <summary>
196         /// Reconstruct block header from item data.
197         /// </summary>
198         public CBlockHeader BlockHeader
199         {
200             get
201             {
202                 CBlockHeader header = new CBlockHeader();
203
204                 header.nVersion = nVersion;
205                 header.prevHash = prevHash;
206                 header.merkleRoot = merkleRoot;
207                 header.nTime = nTime;
208                 header.nBits = nBits;
209                 header.nNonce = nNonce;
210
211                 return header;
212             }
213         }
214
215         /// <summary>
216         /// Read block from file.
217         /// </summary>
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)
222         {
223             var buffer = new byte[nBlockSize];
224             block = null;
225
226             try
227             {
228                 reader.Seek(nBlockPos, SeekOrigin.Begin);
229
230                 if (nBlockSize != reader.Read(buffer, 0, nBlockSize))
231                 {
232                     return false;
233                 }
234
235                 block = new CBlock(buffer);
236
237                 return true;
238             }
239             catch (IOException)
240             {
241                 // I/O error
242                 return false;
243             }
244             catch (BlockException)
245             {
246                 // Constructor exception
247                 return false;
248             }
249         }
250
251         /// <summary>
252         /// Writes given block to file and prepares cursor object for insertion into the database.
253         /// </summary>
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)
258         {
259             try
260             {
261                 byte[] blockBytes = block;
262
263                 var magicBytes = BitConverter.GetBytes(CBlockStore.nMagicNumber);
264                 var blkLenBytes = BitConverter.GetBytes(blockBytes.Length);
265
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);
270
271                 // Save block size and current position in the block cursor fields.
272                 nBlockPos = writer.Position;
273                 nBlockSize = blockBytes.Length;
274
275                 // Write block and flush the stream.
276                 writer.Write(blockBytes, 0, blockBytes.Length);
277                 writer.Flush();
278
279                 return true;
280             }
281             catch (IOException)
282             {
283                 // I/O error
284                 return false;
285             }
286             catch (Exception)
287             {
288                 // Some serialization error
289                 return false;
290             }
291         }
292
293         /// <summary>
294         /// Previous block cursor
295         /// </summary>
296         [Ignore]
297         public CBlockStoreItem prev
298         {
299             get { return CBlockStore.Instance.GetMapCursor(prevHash); }
300         }
301
302         /// <summary>
303         /// Next block cursor
304         /// </summary>
305         [Ignore]
306         public CBlockStoreItem next
307         {
308             get
309             {
310                 if (nextHash == null)
311                 {
312                     return null;
313                 }
314
315                 return CBlockStore.Instance.GetMapCursor(nextHash);
316             }
317             set
318             {
319                 nextHash = value.Hash;
320
321                 CBlockStore.Instance.UpdateMapCursor(this);
322             }
323         }
324
325         [Ignore]
326         bool IsInMainChain
327         {
328             get { return (next != null); }
329         }
330
331         /// <summary>
332         /// STake modifier generation flag
333         /// </summary>
334         [Ignore]
335         public bool GeneratedStakeModifier
336         {
337             get { return (BlockTypeFlag & BlockType.BLOCK_STAKE_MODIFIER) != 0; }
338         }
339
340         /// <summary>
341         /// Stake entropy bit
342         /// </summary>
343         [Ignore]
344         public uint StakeEntropyBit
345         {
346             get { return ((uint)(BlockTypeFlag & BlockType.BLOCK_STAKE_ENTROPY) >> 1); }
347         }
348
349         /// <summary>
350         /// Sets stake modifier and flag.
351         /// </summary>
352         /// <param name="nModifier">New stake modifier.</param>
353         /// <param name="fGeneratedStakeModifier">Set generation flag?</param>
354         public void SetStakeModifier(long nModifier, bool fGeneratedStakeModifier)
355         {
356             nStakeModifier = nModifier;
357             if (fGeneratedStakeModifier)
358                 BlockTypeFlag |= BlockType.BLOCK_STAKE_MODIFIER;
359         }
360
361         /// <summary>
362         /// Set entropy bit.
363         /// </summary>
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)
367         {
368             if (nEntropyBit > 1)
369                 return false;
370             BlockTypeFlag |= (nEntropyBit != 0 ? BlockType.BLOCK_STAKE_ENTROPY : 0);
371             return true;
372         }
373
374         /// <summary>
375         /// Set proof-of-stake flag.
376         /// </summary>
377         public void SetProofOfStake()
378         {
379             BlockTypeFlag |= BlockType.BLOCK_PROOF_OF_STAKE;
380         }
381
382         /// <summary>
383         /// Block has no proof-of-stake flag.
384         /// </summary>
385         [Ignore]
386         public bool IsProofOfWork
387         {
388             get { return (BlockTypeFlag & BlockType.BLOCK_PROOF_OF_STAKE) == 0; }
389         }
390
391         /// <summary>
392         /// Block has proof-of-stake flag set.
393         /// </summary>
394         [Ignore]
395         public bool IsProofOfStake
396         {
397             get { return (BlockTypeFlag & BlockType.BLOCK_PROOF_OF_STAKE) != 0; }
398         }
399
400         /// <summary>
401         /// Block trust score.
402         /// </summary>
403         [Ignore]
404         public uint256 nBlockTrust
405         {
406             get
407             {
408                 // Return 1 for the first 12 blocks
409                 if (prev == null || prev.nHeight < 12)
410                 {
411                     return 1;
412                 }
413
414                 uint256 nTarget = 0;
415                 nTarget.Compact = nBits;
416
417                 CBlockStoreItem currentIndex = prev;
418
419                 if (IsProofOfStake)
420                 {
421                     var nNewTrust = (new uint256(1) << 256) / (nTarget + 1);
422
423                     // Return 1/3 of score if parent block is not the PoW block
424                     if (!prev.IsProofOfWork)
425                     {
426                         return nNewTrust / 3;
427                     }
428
429                     int nPoWCount = 0;
430
431                     // Check last 12 blocks type
432                     while (prev.nHeight - currentIndex.nHeight < 12)
433                     {
434                         if (currentIndex.IsProofOfWork)
435                         {
436                             nPoWCount++;
437                         }
438                         currentIndex = currentIndex.prev;
439                     }
440
441                     // Return 1/3 of score if less than 3 PoW blocks found
442                     if (nPoWCount < 3)
443                     {
444                         return nNewTrust / 3;
445                     }
446
447                     return nNewTrust;
448                 }
449                 else
450                 {
451                     // Calculate work amount for block
452                     var nPoWTrust = NetInfo.nPoWBase / (nTarget + 1);
453
454                     // Set nPowTrust to 1 if we are checking PoS block or PoW difficulty is too low
455                     nPoWTrust = (IsProofOfStake || !nPoWTrust) ? 1 : nPoWTrust;
456
457                     var nLastBlockTrust = prev.nChainTrust - prev.prev.nChainTrust;
458
459                     // Return nPoWTrust + 2/3 of previous block score if two parent blocks are not PoS blocks
460                     if (!prev.IsProofOfStake || !prev.prev.IsProofOfStake)
461                     {
462                         return nPoWTrust + (2 * nLastBlockTrust / 3);
463                     }
464
465                     int nPoSCount = 0;
466
467                     // Check last 12 blocks type
468                     while (prev.nHeight - currentIndex.nHeight < 12)
469                     {
470                         if (currentIndex.IsProofOfStake)
471                         {
472                             nPoSCount++;
473                         }
474                         currentIndex = currentIndex.prev;
475                     }
476
477                     // Return nPoWTrust + 2/3 of previous block score if less than 7 PoS blocks found
478                     if (nPoSCount < 7)
479                     {
480                         return nPoWTrust + (2 * nLastBlockTrust / 3);
481                     }
482
483                     nTarget.Compact = prev.nBits;
484
485                     if (!nTarget)
486                     {
487                         return 0;
488                     }
489
490                     var nNewTrust = (new uint256(1) << 256) / (nTarget + 1);
491
492                     // Return nPoWTrust + full trust score for previous block nBits
493                     return nPoWTrust + nNewTrust;
494                 }
495             }
496         }
497
498         /// <summary>
499         /// Stake modifier checksum
500         /// </summary>
501         public uint nStakeModifierChecksum;
502
503         /// <summary>
504         /// Chain trust score
505         /// </summary>
506         [Ignore]
507         public uint256 nChainTrust
508         {
509             get { return Interop.AppendWithZeros(ChainTrust); }
510             set { ChainTrust = Interop.TrimArray(value); }
511         }
512
513         public long nMint { get; internal set; }
514         public long nMoneySupply { get; internal set; }
515     }
516
517     /// <summary>
518     /// Block type.
519     /// </summary>
520     public enum BlockType
521     {
522         BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block
523         BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier
524         BLOCK_STAKE_MODIFIER = (1 << 2), // regenerated stake modifier
525     };
526
527     /// <summary>
528     /// Transaction type.
529     /// </summary>
530     public enum TxFlags : byte
531     {
532         TX_COINBASE,
533         TX_COINSTAKE,
534         TX_USER
535     }
536
537     /// <summary>
538     /// Output flags.
539     /// </summary>
540     public enum OutputFlags : byte
541     {
542         AVAILABLE, // Unspent output
543         SPENT      // Spent output
544     }
545
546     [Table("MerkleNodes")]
547     public class CMerkleNode
548     {
549         #region IMerkleNode
550         /// <summary>
551         /// Node identifier
552         /// </summary>
553         [PrimaryKey, AutoIncrement]
554         public long nMerkleNodeID { get; set; }
555
556         /// <summary>
557         /// Reference to parent block database item.
558         /// </summary>
559         [ForeignKey(typeof(CBlockStoreItem), Name = "ItemId")]
560         public long nParentBlockID { get; set; }
561
562         /// <summary>
563         /// Transaction timestamp
564         /// </summary>
565         [Column("nTime")]
566         public uint nTime { get; set; }
567
568         /// <summary>
569         /// Transaction type flag
570         /// </summary>
571         [Column("TransactionFlags")]
572         public TxFlags TransactionFlags { get; set; }
573
574         /// <summary>
575         /// Transaction hash
576         /// </summary>
577         [Column("TransactionHash")]
578         public byte[] TransactionHash { get; set; }
579
580         /// <summary>
581         /// Transaction offset from the beginning of block header, encoded in VarInt format.
582         /// </summary>
583         [Column("TxOffset")]
584         public byte[] TxOffset { get; set; }
585
586         /// <summary>
587         /// Transaction size, encoded in VarInt format.
588         /// </summary>
589         [Column("TxSize")]
590         public byte[] TxSize { get; set; }
591         #endregion
592
593         /// <summary>
594         /// Read transaction from file.
595         /// </summary>
596         /// <param name="reader">Stream with read access.</param>
597         /// <param name="tx">CTransaction reference.</param>
598         /// <returns>Result</returns>
599         public bool ReadFromFile(ref Stream reader, long nBlockPos, out CTransaction tx)
600         {
601             var buffer = new byte[CTransaction.nMaxTxSize];
602
603             tx = null;
604
605             try
606             {
607                 reader.Seek(nBlockPos + nTxOffset, SeekOrigin.Begin); // Seek to transaction offset
608
609                 if (nTxSize != reader.Read(buffer, 0, (int)nTxSize))
610                 {
611                     return false;
612                 }
613
614                 tx = new CTransaction(buffer);
615
616                 return true;
617             }
618             catch (IOException)
619             {
620                 // I/O error
621                 return false;
622             }
623             catch (TransactionConstructorException)
624             {
625                 // Constructor error
626                 return false;
627             }
628         }
629
630         /// <summary>
631         /// Transaction offset accessor
632         /// </summary>
633         [Ignore]
634         public long nTxOffset
635         {
636             get { return (long)VarInt.DecodeVarInt(TxOffset); }
637             private set { TxOffset = VarInt.EncodeVarInt(value); }
638         }
639
640         /// <summary>
641         /// Transaction size accessor
642         /// </summary>
643         [Ignore]
644         public uint nTxSize
645         {
646             get { return (uint)VarInt.DecodeVarInt(TxSize); }
647             private set { TxSize = VarInt.EncodeVarInt(value); }
648         }
649
650         [Ignore]
651         public bool IsCoinBase
652         {
653             get { return TransactionFlags == TxFlags.TX_COINBASE; }
654         }
655
656         [Ignore]
657         public bool IsCoinStake
658         {
659             get { return TransactionFlags == TxFlags.TX_COINSTAKE; }
660         }
661
662         public CMerkleNode()
663         {
664         }
665
666         public CMerkleNode(CTransaction tx)
667         {
668             nTime = tx.nTime;
669
670             nTxOffset = -1;
671             nParentBlockID = -1;
672
673             nTxSize = tx.Size;
674             TransactionHash = tx.Hash;
675
676             if (tx.IsCoinBase)
677             {
678                 TransactionFlags = TxFlags.TX_COINBASE;
679             }
680             else if (tx.IsCoinStake)
681             {
682                 TransactionFlags = TxFlags.TX_COINSTAKE;
683             }
684             else
685             {
686                 TransactionFlags = TxFlags.TX_USER;
687             }
688         }
689
690         public CMerkleNode(long nBlockId, long nOffset, CTransaction tx)
691         {
692             nTime = tx.nTime;
693
694             nParentBlockID = nBlockId;
695
696             nTxOffset = nOffset;
697             nTxSize = tx.Size;
698             TransactionHash = tx.Hash;
699
700             if (tx.IsCoinBase)
701             {
702                 TransactionFlags |= TxFlags.TX_COINBASE;
703             }
704             else if (tx.IsCoinStake)
705             {
706                 TransactionFlags |= TxFlags.TX_COINSTAKE;
707             }
708             else
709             {
710                 TransactionFlags |= TxFlags.TX_USER;
711             }
712         }
713
714     }
715
716     [Table("Outputs")]
717     public class TxOutItem
718     {
719         /// <summary>
720         /// Outpoint identifier.
721         /// </summary>
722         [PrimaryKey, AutoIncrement]
723         public long nOutpointID { get; set; }
724
725         /// <summary>
726         /// Reference to transaction item.
727         /// </summary>
728         [ForeignKey(typeof(CMerkleNode), Name = "nMerkleNodeID")]
729         public long nMerkleNodeID { get; set; }
730
731         /// <summary>
732         /// Output flags
733         /// </summary>
734         public OutputFlags outputFlags { get; set; }
735
736         /// <summary>
737         /// Output number in VarInt format.
738         /// </summary>
739         public byte[] OutputNumber { get; set; }
740
741         /// <summary>
742         /// Output value in VarInt format.
743         /// </summary>
744         public byte[] OutputValue { get; set; }
745
746         /// <summary>
747         /// Second half of script which contains spending instructions.
748         /// </summary>
749         public byte[] scriptPubKey { get; set; }
750
751         /// <summary>
752         /// Getter for output number.
753         /// </summary>
754         [Ignore]
755         public uint nOut
756         {
757             get { return (uint)VarInt.DecodeVarInt(OutputNumber); }
758             set { OutputNumber = VarInt.EncodeVarInt(value); }
759         }
760
761         /// <summary>
762         /// Getter for output value.
763         /// </summary>
764         [Ignore]
765         public long nValue
766         {
767             get { return VarInt.DecodeVarInt(OutputValue); }
768             set { OutputValue = VarInt.EncodeVarInt(value); }
769         }
770
771         /// <summary>
772         /// Getter ans setter for IsSpent flag.
773         /// </summary>
774         [Ignore]
775         public bool IsSpent
776         {
777             get { return (outputFlags & OutputFlags.SPENT) != 0; }
778             set { outputFlags |= value ? OutputFlags.SPENT : OutputFlags.AVAILABLE; }
779         }
780     }
781
782
783     /// <summary>
784     /// TxOut + transaction hash
785     /// </summary>
786     public class InputsJoin : TxOutItem
787     {
788         public byte[] TransactionHash { get; set; }
789
790         /// <summary>
791         /// To avoid awkwardness of sqlite wrapper.
792         /// </summary>
793         /// <returns></returns>
794         public TxOutItem getTxOutItem()
795         {
796             return new TxOutItem()
797             {
798                 nOutpointID = nOutpointID,
799                 nMerkleNodeID = nMerkleNodeID,
800                 outputFlags = outputFlags,
801                 OutputNumber = OutputNumber,
802                 OutputValue = OutputValue,
803                 scriptPubKey = scriptPubKey
804             };
805         }
806     }
807
808
809
810 }