CBlock: Fix null reference in copy constructor
[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.Linq;
21 using System.Text;
22 using System.Collections.Generic;
23 using System.Security.Cryptography;
24
25 namespace Novacoin
26 {
27         /// <summary>
28         /// Represents the block. Block consists of header, transaction array and header signature.
29         /// </summary>
30         public class CBlock
31         {
32                 /// <summary>
33                 /// Block header.
34                 /// </summary>
35                 public CBlockHeader header;
36
37                 /// <summary>
38                 /// Transactions array.
39                 /// </summary>
40                 public CTransaction[] vtx;
41
42         /// <summary>
43         /// Block header signature.
44         /// </summary>
45         public byte[] signature = new byte[0];
46
47         public CBlock(CBlock b)
48         {
49             header = new CBlockHeader(b.header);
50             vtx = new CTransaction[b.vtx.Length];
51
52             for (int i = 0; i < b.vtx.Length; i++)
53             {
54                 vtx[i] = new CTransaction(b.vtx[i]);
55             }
56
57             b.signature.CopyTo(signature, 0);
58         }
59
60         /// <summary>
61         /// Parse byte sequence and initialize new block instance
62         /// </summary>
63         /// <param name="blockBytes"></param>
64                 public CBlock (byte[] blockBytes)
65                 {
66             ByteQueue wBytes = new ByteQueue(blockBytes);
67
68             // Fill the block header fields
69             header = new CBlockHeader(wBytes.Get(80));
70
71             // Parse transactions list
72             vtx = CTransaction.ReadTransactionsList(ref wBytes);
73
74             // Read block signature
75             signature = wBytes.Get((int)wBytes.GetVarInt());
76                 }
77
78         public CBlock()
79         {
80             // Initialize empty array of transactions. Please note that such 
81             // configuration is not valid real block since it has to provide 
82             // at least one transaction.
83             vtx = new CTransaction[0];
84         }
85
86         /// <summary>
87         /// Is this a Proof-of-Stake block?
88         /// </summary>
89         public bool IsProofOfStake
90         {
91             get
92             {
93                 return (vtx.Length > 1 && vtx[1].IsCoinStake);
94             }
95         }
96
97         /// <summary>
98         /// Was this signed correctly?
99         /// </summary>
100         public bool SignatureOK
101         {
102             get
103             {
104                 if (IsProofOfStake)
105                 {
106                     if (signature.Length == 0)
107                     {
108                         return false; // No signature
109                     }
110
111                     txnouttype whichType;
112                     IList<byte[]> solutions;
113
114                     if (!ScriptCode.Solver(vtx[1].vout[1].scriptPubKey, out whichType, out solutions))
115                     {
116                         return false; // No solutions found
117                     }
118
119                     if (whichType == txnouttype.TX_PUBKEY)
120                     {
121                         CPubKey pubkey;
122
123                         try
124                         {
125                             pubkey = new CPubKey(solutions[0]);
126                         }
127                         catch (Exception)
128                         {
129                             return false; // Error while loading public key
130                         }
131
132                         return pubkey.VerifySignature(header.Hash, signature);
133                     }
134                 }
135                 else
136                 {
137                     // Proof-of-Work blocks have no signature
138
139                     return true;
140                 }
141
142                 return false;
143             }
144         }
145
146         /// <summary>
147         /// Get instance as sequence of bytes
148         /// </summary>
149         /// <returns>Byte sequence</returns>
150         public static implicit operator byte[] (CBlock b)
151         {
152             var r = new List<byte>();
153
154             r.AddRange((byte[])b.header);
155             r.AddRange(VarInt.EncodeVarInt(b.vtx.LongLength)); // transactions count
156
157             foreach (var tx in b.vtx)
158             {
159                 r.AddRange((byte[])tx);
160             }
161
162             r.AddRange(VarInt.EncodeVarInt(b.signature.LongLength));
163             r.AddRange(b.signature);
164
165             return r.ToArray();
166         }
167
168         /// <summary>
169         /// Merkle root
170         /// </summary>
171         public Hash256 hashMerkleRoot
172         {
173             get {
174                 
175                 var merkleTree = new List<byte>();
176
177                 foreach (var tx in vtx)
178                 {
179                     merkleTree.AddRange(Hash256.ComputeRaw256(tx));
180                 }
181
182                 int levelOffset = 0;
183                 for (int nLevelSize = vtx.Length; nLevelSize > 1; nLevelSize = (nLevelSize + 1) / 2)
184                 {
185                     for (int nLeft = 0; nLeft < nLevelSize; nLeft += 2)
186                     {
187                         int nRight = Math.Min(nLeft + 1, nLevelSize - 1);
188
189                         var left = merkleTree.GetRange((levelOffset + nLeft) * 32, 32).ToArray();
190                         var right = merkleTree.GetRange((levelOffset + nRight) * 32, 32).ToArray();
191
192                         merkleTree.AddRange(Hash256.ComputeRaw256(ref left, ref right));
193                     }
194                     levelOffset += nLevelSize;
195                 }
196
197                 return (merkleTree.Count == 0) ? new Hash256() : new Hash256(merkleTree.GetRange(merkleTree.Count-32, 32).ToArray());
198             }
199         }
200
201         public override string ToString()
202         {
203             var sb = new StringBuilder();
204
205             sb.AppendFormat("CBlock(\n header={0},\n", header.ToString());
206
207             foreach(var tx in vtx)
208             {
209                 sb.AppendFormat("{0}", tx.ToString());
210             }
211
212             if (IsProofOfStake)
213             {
214                 sb.AppendFormat(", signature={0}, signatureOK={1}\n", Interop.ToHex(signature), SignatureOK);
215             }
216
217             sb.Append(")");
218             
219             return sb.ToString();
220         }
221         }
222 }
223