9bf7e360511138be2d36f284f93c73ef58d52cd7
[NovacoinLibrary.git] / Novacoin / CBlock.cs
1 \feff/**
2  *  Novacoin classes library
3  *  Copyright (C) 2015 Alex D. (balthazar.ad@gmail.com)
4
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.
9
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.
14
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/>.
17  */
18
19 using System;
20 using System.Text;
21 using System.Collections.Generic;
22 using System.Diagnostics.Contracts;
23 using System.IO;
24
25 namespace Novacoin
26 {
27     [Serializable]
28     public class BlockException : Exception
29     {
30         public BlockException()
31         {
32         }
33
34         public BlockException(string message)
35                 : base(message)
36         {
37         }
38
39         public BlockException(string message, Exception inner)
40                 : base(message, inner)
41         {
42         }
43     }
44
45     /// <summary>
46     /// Represents the block. Block consists of header, transaction array and header signature.
47     /// </summary>
48     public class CBlock
49         {
50         /// <summary>
51         /// Maximum block size is 1Mb.
52         /// </summary>
53         public const uint nMaxBlockSize = 1000000;
54
55         /// <summary>
56         /// Sanity threshold for amount of sigops.
57         /// </summary>
58         public const uint nMaxSigOps = 20000;
59
60                 /// <summary>
61                 /// Block header.
62                 /// </summary>
63                 public CBlockHeader header;
64
65                 /// <summary>
66                 /// Transactions array.
67                 /// </summary>
68                 public CTransaction[] vtx;
69
70         /// <summary>
71         /// Block header signature.
72         /// </summary>
73         public byte[] signature = new byte[0];
74
75         /// <summary>
76         /// Copy constructor.
77         /// </summary>
78         /// <param name="b">CBlock instance.</param>
79         public CBlock(CBlock b)
80         {
81             header = new CBlockHeader(b.header);
82             vtx = new CTransaction[b.vtx.Length];
83
84             for (int i = 0; i < b.vtx.Length; i++)
85             {
86                 vtx[i] = new CTransaction(b.vtx[i]);
87             }
88
89             signature = new byte[b.signature.Length];
90             b.signature.CopyTo(signature, 0);
91         }
92
93         /// <summary>
94         /// Parse byte sequence and initialize new block instance
95         /// </summary>
96         /// <param name="blockBytes">Bytes sequence.</param>
97                 public CBlock (byte[] blockBytes)
98                 {
99             try
100             {
101                 var stream = new MemoryStream(blockBytes);
102                 var reader = new BinaryReader(stream);
103
104                 // Fill the block header fields
105                 header = new CBlockHeader(ref reader);               
106
107                 // Parse transactions list
108                 vtx = CTransaction.ReadTransactionsList(ref reader);
109
110                 // Read block signature
111                 signature = reader.ReadBytes((int)VarInt.ReadVarInt(ref reader));
112
113                 reader.Close();
114             }
115             catch (Exception e)
116             {
117                 throw new BlockException("Deserialization failed", e);
118             }
119                 }
120
121         public CBlock()
122         {
123             // Initialize empty array of transactions. Please note that such 
124             // configuration is not valid real block since it has to provide 
125             // at least one transaction.
126             vtx = new CTransaction[0];
127         }
128
129         public bool CheckBlock(bool fCheckPOW = true, bool fCheckMerkleRoot = true, bool fCheckSig = true)
130         {
131             var uniqueTX = new List<uint256>(); // tx hashes
132             uint nSigOps = 0; // total sigops
133
134             // Basic sanity checkings
135             if (vtx.Length == 0 || Size > nMaxBlockSize)
136             {
137                 return false;
138             }
139
140             bool fProofOfStake = IsProofOfStake;
141
142             // First transaction must be coinbase, the rest must not be
143             if (!vtx[0].IsCoinBase)
144             {
145                 return false;
146             }
147
148             if (!vtx[0].CheckTransaction())
149             {
150                 return false;
151             }
152
153             uniqueTX.Add(vtx[0].Hash);
154             nSigOps += vtx[0].LegacySigOpCount;
155
156             if (fProofOfStake)
157             {
158                 // Proof-of-STake related checkings. Note that we know here that 1st transactions is coinstake. We don't need 
159                 //   check the type of 1st transaction because it's performed earlier by IsProofOfStake()
160
161                 // nNonce must be zero for proof-of-stake blocks
162                 if (header.nNonce != 0)
163                 {
164                     return false;
165                 }
166
167                 // Coinbase output should be empty if proof-of-stake block
168                 if (vtx[0].vout.Length != 1 || !vtx[0].vout[0].IsEmpty)
169                 {
170                     return false;
171                 }
172
173                 // Check coinstake timestamp
174                 if (header.nTime != vtx[1].nTime)
175                 {
176                     return false;
177                 }
178
179                 // Check proof-of-stake block signature
180                 if (fCheckSig && !SignatureOK)
181                 {
182                     return false;
183                 }
184
185                 if (!vtx[1].CheckTransaction())
186                 {
187                     return false;
188                 }
189
190                 uniqueTX.Add(vtx[1].Hash);
191                 nSigOps += vtx[1].LegacySigOpCount;
192             }
193             else
194             {
195                 // Check proof of work matches claimed amount
196                 if (fCheckPOW && !CheckProofOfWork(header.Hash, header.nBits))
197                 {
198                     return false;
199                 }
200
201                 // Check timestamp
202                 if (header.nTime > NetUtils.FutureDrift(NetUtils.GetAdjustedTime()))
203                 {
204                     return false;
205                 }
206
207                 // Check coinbase timestamp
208                 if (header.nTime < NetUtils.PastDrift(vtx[0].nTime))
209                 {
210                     return false;
211                 }
212             }
213
214             // Iterate all transactions starting from second for proof-of-stake block 
215             //    or first for proof-of-work block
216             for (int i = fProofOfStake ? 2 : 1; i < vtx.Length; i++)
217             {
218                 var tx = vtx[i];
219
220                 // Reject coinbase transactions at non-zero index
221                 if (tx.IsCoinBase)
222                 {
223                     return false;
224                 }
225
226                 // Reject coinstake transactions at index != 1
227                 if (tx.IsCoinStake)
228                 {
229                     return false;
230                 }
231
232                 // Check transaction timestamp
233                 if (header.nTime < tx.nTime)
234                 {
235                     return false;
236                 }
237
238                 // Check transaction consistency
239                 if (!tx.CheckTransaction())
240                 {
241                     return false;
242                 }
243
244                 // Add transaction hash into list of unique transaction IDs
245                 uniqueTX.Add(tx.Hash);
246
247                 // Calculate sigops count
248                 nSigOps += tx.LegacySigOpCount;
249             }
250
251             // Check for duplicate txids. 
252             if (uniqueTX.Count != vtx.Length)
253             {
254                 return false;
255             }
256
257             // Reject block if validation would consume too much resources.
258             if (nSigOps > nMaxSigOps)
259             {
260                 return false;
261             }
262
263             // Check merkle root
264             if (fCheckMerkleRoot && hashMerkleRoot != header.merkleRoot)
265             {
266                 return false;
267             }
268
269             return true;
270         }
271
272         private bool CheckProofOfWork(uint256 hash, uint nBits)
273         {
274             uint256 nTarget = new uint256();
275             nTarget.Compact = nBits;
276
277             // Check range
278             if (nTarget > NetUtils.nProofOfWorkLimit)
279             {
280                 // nBits below minimum work
281                 return false; 
282             }
283
284             // Check proof of work matches claimed amount
285             if (hash > nTarget)
286             {
287                 //  hash doesn't match nBits
288                 return false;
289             }
290
291             return true;
292         }
293
294         /// <summary>
295         /// Is this a Proof-of-Stake block?
296         /// </summary>
297         public bool IsProofOfStake
298         {
299             get
300             {
301                 return (vtx.Length > 1 && vtx[1].IsCoinStake);
302             }
303         }
304
305         /// <summary>
306         /// Was this signed correctly?
307         /// </summary>
308         public bool SignatureOK
309         {
310             get
311             {
312                 if (IsProofOfStake)
313                 {
314                     if (signature.Length == 0)
315                     {
316                         return false; // No signature
317                     }
318
319                     txnouttype whichType;
320                     IList<byte[]> solutions;
321
322                     if (!ScriptCode.Solver(vtx[1].vout[1].scriptPubKey, out whichType, out solutions))
323                     {
324                         return false; // No solutions found
325                     }
326
327                     if (whichType == txnouttype.TX_PUBKEY)
328                     {
329                         CPubKey pubkey;
330
331                         try
332                         {
333                             pubkey = new CPubKey(solutions[0]);
334                         }
335                         catch (Exception)
336                         {
337                             return false; // Error while loading public key
338                         }
339
340                         return pubkey.VerifySignature(header.Hash, signature);
341                     }
342                 }
343                 else
344                 {
345                     // Proof-of-Work blocks have no signature
346
347                     return true;
348                 }
349
350                 return false;
351             }
352         }
353
354         /// <summary>
355         /// Get instance as sequence of bytes
356         /// </summary>
357         /// <returns>Byte sequence</returns>
358         public static implicit operator byte[] (CBlock b)
359         {
360             var stream = new MemoryStream();
361             var writer = new BinaryWriter(stream);
362
363             writer.Write(b.header);
364             writer.Write(VarInt.EncodeVarInt(b.vtx.LongLength));
365
366             foreach (var tx in b.vtx)
367             {
368                 writer.Write(tx);
369             }
370
371             writer.Write(VarInt.EncodeVarInt(b.signature.LongLength));
372             writer.Write(b.signature);
373
374             var resultBytes = stream.ToArray();
375
376             writer.Close();
377
378             return resultBytes;
379         }
380
381         /// <summary>
382         /// Serialized size
383         /// </summary>
384         public int Size
385         {
386             get
387             {
388                 int nSize = 80 + VarInt.GetEncodedSize(vtx.Length); // CBlockHeader + NumTx
389
390                 foreach (var tx in vtx)
391                 {
392                     nSize += tx.Size;
393                 }
394
395                 nSize += VarInt.GetEncodedSize(signature.Length) + signature.Length;
396
397                 return nSize;
398             }
399         }
400
401         /// <summary>
402         /// Get transaction offset inside block.
403         /// </summary>
404         /// <param name="nTx">Transaction index.</param>
405         /// <returns>Offset in bytes from the beginning of block header.</returns>
406         public int GetTxOffset(int nTx)
407         {
408             Contract.Requires<ArgumentException>(nTx >= 0 && nTx < vtx.Length, "Transaction index you've specified is incorrect.");
409
410             int nOffset = 80 + VarInt.GetEncodedSize(vtx.Length); // CBlockHeader + NumTx
411
412             for (int i = 0; i < nTx; i++)
413             {
414                 nOffset += vtx[i].Size;
415             }
416
417             return nOffset;
418         }
419
420         /// <summary>
421         /// Merkle root
422         /// </summary>
423         public uint256 hashMerkleRoot
424         {
425             get {
426                 
427                 var merkleTree = new List<byte>();
428
429                 foreach (var tx in vtx)
430                 {
431                     merkleTree.AddRange(CryptoUtils.ComputeHash256(tx));
432                 }
433
434                 int levelOffset = 0;
435                 for (int nLevelSize = vtx.Length; nLevelSize > 1; nLevelSize = (nLevelSize + 1) / 2)
436                 {
437                     for (int nLeft = 0; nLeft < nLevelSize; nLeft += 2)
438                     {
439                         int nRight = Math.Min(nLeft + 1, nLevelSize - 1);
440
441                         var left = merkleTree.GetRange((levelOffset + nLeft) * 32, 32).ToArray();
442                         var right = merkleTree.GetRange((levelOffset + nRight) * 32, 32).ToArray();
443
444                         merkleTree.AddRange(CryptoUtils.ComputeHash256(ref left, ref right));
445                     }
446                     levelOffset += nLevelSize;
447                 }
448
449                 return (merkleTree.Count == 0) ? 0 : (uint256)merkleTree.GetRange(merkleTree.Count-32, 32).ToArray();
450             }
451         }
452
453         public override string ToString()
454         {
455             var sb = new StringBuilder();
456
457             sb.AppendFormat("CBlock(\n header={0},\n", header.ToString());
458
459             foreach(var tx in vtx)
460             {
461                 sb.AppendFormat("{0}", tx.ToString());
462             }
463
464             if (IsProofOfStake)
465             {
466                 sb.AppendFormat(", signature={0}, signatureOK={1}\n", Interop.ToHex(signature), SignatureOK);
467             }
468
469             sb.Append(")");
470             
471             return sb.ToString();
472         }
473         }
474 }
475