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