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