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