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