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