3008db8eccdf6b6216157ce8196b27fcf82b5bc7
[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
24 namespace Novacoin
25 {
26     [Serializable]
27     public class BlockConstructorException : Exception
28     {
29         public BlockConstructorException()
30         {
31         }
32
33         public BlockConstructorException(string message)
34                 : base(message)
35         {
36         }
37
38         public BlockConstructorException(string message, Exception inner)
39                 : base(message, inner)
40         {
41         }
42     }
43
44     /// <summary>
45     /// Represents the block. Block consists of header, transaction array and header signature.
46     /// </summary>
47     public class CBlock
48         {
49                 /// <summary>
50                 /// Block header.
51                 /// </summary>
52                 public CBlockHeader header;
53
54                 /// <summary>
55                 /// Transactions array.
56                 /// </summary>
57                 public CTransaction[] vtx;
58
59         /// <summary>
60         /// Block header signature.
61         /// </summary>
62         public byte[] signature = new byte[0];
63
64         /// <summary>
65         /// Copy constructor.
66         /// </summary>
67         /// <param name="b">CBlock instance.</param>
68         public CBlock(CBlock b)
69         {
70             header = new CBlockHeader(b.header);
71             vtx = new CTransaction[b.vtx.Length];
72
73             for (int i = 0; i < b.vtx.Length; i++)
74             {
75                 vtx[i] = new CTransaction(b.vtx[i]);
76             }
77
78             b.signature.CopyTo(signature, 0);
79         }
80
81         /// <summary>
82         /// Parse byte sequence and initialize new block instance
83         /// </summary>
84         /// <param name="blockBytes">Bytes sequence.</param>
85                 public CBlock (byte[] blockBytes)
86                 {
87             try
88             {
89                 ByteQueue wBytes = new ByteQueue(blockBytes);
90
91                 // Fill the block header fields
92                 header = new CBlockHeader(wBytes.Get(80));
93
94                 // Parse transactions list
95                 vtx = CTransaction.ReadTransactionsList(ref wBytes);
96
97                 // Read block signature
98                 signature = wBytes.Get((int)wBytes.GetVarInt());
99             }
100             catch (Exception e)
101             {
102                 throw new BlockConstructorException("Deserealization failed", e);
103             }
104                 }
105
106         public CBlock()
107         {
108             // Initialize empty array of transactions. Please note that such 
109             // configuration is not valid real block since it has to provide 
110             // at least one transaction.
111             vtx = new CTransaction[0];
112         }
113
114         /// <summary>
115         /// Is this a Proof-of-Stake block?
116         /// </summary>
117         public bool IsProofOfStake
118         {
119             get
120             {
121                 return (vtx.Length > 1 && vtx[1].IsCoinStake);
122             }
123         }
124
125         /// <summary>
126         /// Was this signed correctly?
127         /// </summary>
128         public bool SignatureOK
129         {
130             get
131             {
132                 if (IsProofOfStake)
133                 {
134                     if (signature.Length == 0)
135                     {
136                         return false; // No signature
137                     }
138
139                     txnouttype whichType;
140                     IList<byte[]> solutions;
141
142                     if (!ScriptCode.Solver(vtx[1].vout[1].scriptPubKey, out whichType, out solutions))
143                     {
144                         return false; // No solutions found
145                     }
146
147                     if (whichType == txnouttype.TX_PUBKEY)
148                     {
149                         CPubKey pubkey;
150
151                         try
152                         {
153                             pubkey = new CPubKey(solutions[0]);
154                         }
155                         catch (Exception)
156                         {
157                             return false; // Error while loading public key
158                         }
159
160                         return pubkey.VerifySignature(header.Hash, signature);
161                     }
162                 }
163                 else
164                 {
165                     // Proof-of-Work blocks have no signature
166
167                     return true;
168                 }
169
170                 return false;
171             }
172         }
173
174         /// <summary>
175         /// Get instance as sequence of bytes
176         /// </summary>
177         /// <returns>Byte sequence</returns>
178         public static implicit operator byte[] (CBlock b)
179         {
180             var r = new List<byte>();
181
182             r.AddRange((byte[])b.header);
183             r.AddRange(VarInt.EncodeVarInt(b.vtx.LongLength)); // transactions count
184
185             foreach (var tx in b.vtx)
186             {
187                 r.AddRange((byte[])tx);
188             }
189
190             r.AddRange(VarInt.EncodeVarInt(b.signature.LongLength));
191             r.AddRange(b.signature);
192
193             return r.ToArray();
194         }
195
196         /// <summary>
197         /// Serialized size
198         /// </summary>
199         public int Size
200         {
201             get
202             {
203                 int nSize = 80 + VarInt.GetEncodedSize(vtx.Length); // CBlockHeader + NumTx
204
205                 foreach (var tx in vtx)
206                 {
207                     nSize += tx.Size;
208                 }
209
210                 nSize += VarInt.GetEncodedSize(signature.Length) + signature.Length;
211
212                 return nSize;
213             }
214         }
215
216         /// <summary>
217         /// Get transaction offset inside block.
218         /// </summary>
219         /// <param name="nTx">Transaction index.</param>
220         /// <returns>Offset in bytes from the beginning of block header.</returns>
221         public int GetTxOffset(int nTx)
222         {
223             Contract.Requires<ArgumentException>(nTx >= 0 && nTx < vtx.Length, "Transaction index you've specified is incorrect.");
224
225             int nOffset = 80 + VarInt.GetEncodedSize(vtx.Length); // CBlockHeader + NumTx
226
227             for (int i = 0; i < nTx; i++)
228             {
229                 nOffset += vtx[i].Size;
230             }
231
232             return nOffset;
233         }
234
235         /// <summary>
236         /// Merkle root
237         /// </summary>
238         public Hash256 hashMerkleRoot
239         {
240             get {
241                 
242                 var merkleTree = new List<byte>();
243
244                 foreach (var tx in vtx)
245                 {
246                     merkleTree.AddRange(Hash256.ComputeRaw256(tx));
247                 }
248
249                 int levelOffset = 0;
250                 for (int nLevelSize = vtx.Length; nLevelSize > 1; nLevelSize = (nLevelSize + 1) / 2)
251                 {
252                     for (int nLeft = 0; nLeft < nLevelSize; nLeft += 2)
253                     {
254                         int nRight = Math.Min(nLeft + 1, nLevelSize - 1);
255
256                         var left = merkleTree.GetRange((levelOffset + nLeft) * 32, 32).ToArray();
257                         var right = merkleTree.GetRange((levelOffset + nRight) * 32, 32).ToArray();
258
259                         merkleTree.AddRange(Hash256.ComputeRaw256(ref left, ref right));
260                     }
261                     levelOffset += nLevelSize;
262                 }
263
264                 return (merkleTree.Count == 0) ? new Hash256() : new Hash256(merkleTree.GetRange(merkleTree.Count-32, 32).ToArray());
265             }
266         }
267
268         public override string ToString()
269         {
270             var sb = new StringBuilder();
271
272             sb.AppendFormat("CBlock(\n header={0},\n", header.ToString());
273
274             foreach(var tx in vtx)
275             {
276                 sb.AppendFormat("{0}", tx.ToString());
277             }
278
279             if (IsProofOfStake)
280             {
281                 sb.AppendFormat(", signature={0}, signatureOK={1}\n", Interop.ToHex(signature), SignatureOK);
282             }
283
284             sb.Append(")");
285             
286             return sb.ToString();
287         }
288         }
289 }
290