nMaxSigOps constant.
[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<Hash256>(); // 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(ScryptHash256 hash, uint nBits)
273         {
274             // TODO: stub!
275
276             return true;
277         }
278
279         /// <summary>
280         /// Is this a Proof-of-Stake block?
281         /// </summary>
282         public bool IsProofOfStake
283         {
284             get
285             {
286                 return (vtx.Length > 1 && vtx[1].IsCoinStake);
287             }
288         }
289
290         /// <summary>
291         /// Was this signed correctly?
292         /// </summary>
293         public bool SignatureOK
294         {
295             get
296             {
297                 if (IsProofOfStake)
298                 {
299                     if (signature.Length == 0)
300                     {
301                         return false; // No signature
302                     }
303
304                     txnouttype whichType;
305                     IList<byte[]> solutions;
306
307                     if (!ScriptCode.Solver(vtx[1].vout[1].scriptPubKey, out whichType, out solutions))
308                     {
309                         return false; // No solutions found
310                     }
311
312                     if (whichType == txnouttype.TX_PUBKEY)
313                     {
314                         CPubKey pubkey;
315
316                         try
317                         {
318                             pubkey = new CPubKey(solutions[0]);
319                         }
320                         catch (Exception)
321                         {
322                             return false; // Error while loading public key
323                         }
324
325                         return pubkey.VerifySignature(header.Hash, signature);
326                     }
327                 }
328                 else
329                 {
330                     // Proof-of-Work blocks have no signature
331
332                     return true;
333                 }
334
335                 return false;
336             }
337         }
338
339         /// <summary>
340         /// Get instance as sequence of bytes
341         /// </summary>
342         /// <returns>Byte sequence</returns>
343         public static implicit operator byte[] (CBlock b)
344         {
345             var stream = new MemoryStream();
346             var writer = new BinaryWriter(stream);
347
348             writer.Write(b.header);
349             writer.Write(VarInt.EncodeVarInt(b.vtx.LongLength));
350
351             foreach (var tx in b.vtx)
352             {
353                 writer.Write(tx);
354             }
355
356             writer.Write(VarInt.EncodeVarInt(b.signature.LongLength));
357             writer.Write(b.signature);
358
359             var resultBytes = stream.ToArray();
360
361             writer.Close();
362
363             return resultBytes;
364         }
365
366         /// <summary>
367         /// Serialized size
368         /// </summary>
369         public int Size
370         {
371             get
372             {
373                 int nSize = 80 + VarInt.GetEncodedSize(vtx.Length); // CBlockHeader + NumTx
374
375                 foreach (var tx in vtx)
376                 {
377                     nSize += tx.Size;
378                 }
379
380                 nSize += VarInt.GetEncodedSize(signature.Length) + signature.Length;
381
382                 return nSize;
383             }
384         }
385
386         /// <summary>
387         /// Get transaction offset inside block.
388         /// </summary>
389         /// <param name="nTx">Transaction index.</param>
390         /// <returns>Offset in bytes from the beginning of block header.</returns>
391         public int GetTxOffset(int nTx)
392         {
393             Contract.Requires<ArgumentException>(nTx >= 0 && nTx < vtx.Length, "Transaction index you've specified is incorrect.");
394
395             int nOffset = 80 + VarInt.GetEncodedSize(vtx.Length); // CBlockHeader + NumTx
396
397             for (int i = 0; i < nTx; i++)
398             {
399                 nOffset += vtx[i].Size;
400             }
401
402             return nOffset;
403         }
404
405         /// <summary>
406         /// Merkle root
407         /// </summary>
408         public Hash256 hashMerkleRoot
409         {
410             get {
411                 
412                 var merkleTree = new List<byte>();
413
414                 foreach (var tx in vtx)
415                 {
416                     merkleTree.AddRange(Hash256.ComputeRaw256(tx));
417                 }
418
419                 int levelOffset = 0;
420                 for (int nLevelSize = vtx.Length; nLevelSize > 1; nLevelSize = (nLevelSize + 1) / 2)
421                 {
422                     for (int nLeft = 0; nLeft < nLevelSize; nLeft += 2)
423                     {
424                         int nRight = Math.Min(nLeft + 1, nLevelSize - 1);
425
426                         var left = merkleTree.GetRange((levelOffset + nLeft) * 32, 32).ToArray();
427                         var right = merkleTree.GetRange((levelOffset + nRight) * 32, 32).ToArray();
428
429                         merkleTree.AddRange(Hash256.ComputeRaw256(ref left, ref right));
430                     }
431                     levelOffset += nLevelSize;
432                 }
433
434                 return (merkleTree.Count == 0) ? new Hash256() : new Hash256(merkleTree.GetRange(merkleTree.Count-32, 32).ToArray());
435             }
436         }
437
438         public override string ToString()
439         {
440             var sb = new StringBuilder();
441
442             sb.AppendFormat("CBlock(\n header={0},\n", header.ToString());
443
444             foreach(var tx in vtx)
445             {
446                 sb.AppendFormat("{0}", tx.ToString());
447             }
448
449             if (IsProofOfStake)
450             {
451                 sb.AppendFormat(", signature={0}, signatureOK={1}\n", Interop.ToHex(signature), SignatureOK);
452             }
453
454             sb.Append(")");
455             
456             return sb.ToString();
457         }
458         }
459 }
460