Use BouncyCastle hashing functions
[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(Hash256.ComputeRaw256(tx.Bytes));
182                 }
183
184                 int levelOffset = 0;
185                 for (int nLevelSize = vtx.Length; nLevelSize > 1; nLevelSize = (nLevelSize + 1) / 2)
186                 {
187                     for (int nLeft = 0; nLeft < nLevelSize; nLeft += 2)
188                     {
189                         int nRight = Math.Min(nLeft + 1, nLevelSize - 1);
190
191                         var left = merkleTree.GetRange((levelOffset + nLeft) * 32, 32).ToArray();
192                         var right = merkleTree.GetRange((levelOffset + nRight) * 32, 32).ToArray();
193
194                         merkleTree.AddRange(Hash256.ComputeRaw256(ref left, ref right));
195                     }
196                     levelOffset += nLevelSize;
197                 }
198
199                 return (merkleTree.Count == 0) ? new Hash256() : new Hash256(merkleTree.GetRange(merkleTree.Count-32, 32).ToArray());
200             }
201         }
202
203         public override string ToString()
204         {
205             var sb = new StringBuilder();
206
207             sb.AppendFormat("CBlock(\n header={0},\n", header.ToString());
208
209             foreach(var tx in vtx)
210             {
211                 sb.AppendFormat("{0}", tx.ToString());
212             }
213
214             if (IsProofOfStake)
215             {
216                 sb.AppendFormat(", signature={0}, signatureOK={1}\n", Interop.ToHex(signature), SignatureOK);
217             }
218
219             sb.Append(")");
220             
221             return sb.ToString();
222         }
223         }
224 }
225