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