Copy&paste typo fix
[NovacoinLibrary.git] / Novacoin / CTransaction.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
23 namespace Novacoin
24 {
25     [Serializable]
26     public class TransactionConstructorException : Exception
27     {
28         public TransactionConstructorException()
29         {
30         }
31
32         public TransactionConstructorException(string message)
33                 : base(message)
34         {
35         }
36
37         public TransactionConstructorException(string message, Exception inner)
38                 : base(message, inner)
39         {
40         }
41     }
42     
43     /// <summary>
44     /// Represents the transaction. Any transaction must provide one input and one output at least.
45     /// </summary>
46     public class CTransaction
47     {
48         /// <summary>
49         /// Version of transaction schema.
50         /// </summary>
51         public uint nVersion;
52
53         /// <summary>
54         /// Transaction timestamp.
55         /// </summary>
56         public uint nTime;
57
58         /// <summary>
59         /// Array of transaction inputs
60         /// </summary>
61         public CTxIn[] vin;
62
63         /// <summary>
64         /// Array of transaction outputs
65         /// </summary>
66         public CTxOut[] vout;
67
68         /// <summary>
69         /// Block height or timestamp when transaction is final
70         /// </summary>
71         public uint nLockTime;
72
73         /// <summary>
74         /// Initialize an empty instance
75         /// </summary>
76         public CTransaction()
77         {
78             // Initialize empty input and output arrays. Please note that such 
79             // configuration is not valid for real transaction, you have to supply 
80             // at least one input and one output.
81             nVersion = 1;
82             nTime = 0;
83             vin = new CTxIn[0];
84             vout = new CTxOut[0];
85             nLockTime = 0;
86         }
87
88         /// <summary>
89         /// Initialize new instance as a copy of another transaction
90         /// </summary>
91         /// <param name="tx">Transaction to copy from</param>
92         public CTransaction(CTransaction tx)
93         {
94             nVersion = tx.nVersion;
95             nTime = tx.nTime;
96
97             vin = new CTxIn[tx.vin.Length];
98
99             for (int i = 0; i < vin.Length; i++)
100             {
101                 vin[i] = new CTxIn(tx.vin[i]);
102             }
103
104             vout = new CTxOut[tx.vout.Length];
105
106             for (int i = 0; i < vout.Length; i++)
107             {
108                 vout[i] = new CTxOut(tx.vout[i]);
109             }
110
111             nLockTime = tx.nLockTime;
112         }
113
114         public bool VerifyScripts()
115         {
116             if (IsCoinBase)
117             {
118                 return true;
119             }
120
121             CTransaction txPrev = null;
122             for (int i = 0; i < vin.Length; i++)
123             {
124                 var outpoint = vin[i].prevout;
125
126                 if (!CBlockStore.Instance.GetTransaction(outpoint.hash, ref txPrev))
127                     return false;
128
129                 if (!ScriptCode.VerifyScript(vin[i].scriptSig, txPrev.vout[outpoint.n].scriptPubKey, this, i, (int)scriptflag.SCRIPT_VERIFY_P2SH, 0))
130                     return false;
131             }
132
133             return true;
134         }
135
136         /// <summary>
137         /// Parse byte sequence and initialize new instance of CTransaction
138         /// </summary>
139         /// <param name="txBytes">Byte sequence</param>
140                 public CTransaction(byte[] txBytes)
141         {
142             try
143             {
144                 var wBytes = new ByteQueue(txBytes);
145
146                 nVersion = BitConverter.ToUInt32(wBytes.Get(4), 0);
147                 nTime = BitConverter.ToUInt32(wBytes.Get(4), 0);
148
149                 int nInputs = (int)wBytes.GetVarInt();
150                 vin = new CTxIn[nInputs];
151
152                 for (int nCurrentInput = 0; nCurrentInput < nInputs; nCurrentInput++)
153                 {
154                     // Fill inputs array
155                     vin[nCurrentInput] = new CTxIn();
156
157                     vin[nCurrentInput].prevout = new COutPoint(wBytes.Get(36));
158
159                     int nScriptSigLen = (int)wBytes.GetVarInt();
160                     vin[nCurrentInput].scriptSig = new CScript(wBytes.Get(nScriptSigLen));
161
162                     vin[nCurrentInput].nSequence = BitConverter.ToUInt32(wBytes.Get(4), 0);
163                 }
164
165                 int nOutputs = (int)wBytes.GetVarInt();
166                 vout = new CTxOut[nOutputs];
167
168                 for (int nCurrentOutput = 0; nCurrentOutput < nOutputs; nCurrentOutput++)
169                 {
170                     // Fill outputs array
171                     vout[nCurrentOutput] = new CTxOut();
172                     vout[nCurrentOutput].nValue = BitConverter.ToUInt64(wBytes.Get(8), 0);
173
174                     int nScriptPKLen = (int)wBytes.GetVarInt();
175                     vout[nCurrentOutput].scriptPubKey = new CScript(wBytes.Get(nScriptPKLen));
176                 }
177
178                 nLockTime = BitConverter.ToUInt32(wBytes.Get(4), 0);
179             }
180             catch (Exception e)
181             {
182                 throw new TransactionConstructorException("Deserialization failed", e);
183             }
184         }
185
186         /// <summary>
187         /// Serialized size
188         /// </summary>
189         public int Size
190         {
191             get
192             {
193                 int nSize = 12; // nVersion, nTime, nLockLime
194
195                 nSize += VarInt.GetEncodedSize(vin.Length);
196                 nSize += VarInt.GetEncodedSize(vout.Length);
197
198                 foreach (var input in vin)
199                 {
200                     nSize += input.Size;
201                 }
202
203                 foreach (var output in vout)
204                 {
205                     nSize += output.Size;
206                 }
207
208                 return nSize;
209             }
210         }
211
212         /// <summary>
213         /// Read transactions array which is encoded in the block body.
214         /// </summary>
215         /// <param name="wTxBytes">Bytes sequence</param>
216         /// <returns>Transactions array</returns>
217         public static CTransaction[] ReadTransactionsList(ref ByteQueue wTxBytes)
218         {
219             try
220             {
221                 // Read amount of transactions
222                 int nTransactions = (int)wTxBytes.GetVarInt();
223                 var tx = new CTransaction[nTransactions];
224
225                 for (int nTx = 0; nTx < nTransactions; nTx++)
226                 {
227                     // Fill the transactions array
228                     tx[nTx] = new CTransaction();
229
230                     tx[nTx].nVersion = BitConverter.ToUInt32(wTxBytes.Get(4), 0);
231                     tx[nTx].nTime = BitConverter.ToUInt32(wTxBytes.Get(4), 0);
232
233                     // Inputs array
234                     tx[nTx].vin = CTxIn.ReadTxInList(ref wTxBytes);
235
236                     // outputs array
237                     tx[nTx].vout = CTxOut.ReadTxOutList(ref wTxBytes);
238
239                     tx[nTx].nLockTime = BitConverter.ToUInt32(wTxBytes.Get(4), 0);
240                 }
241
242                 return tx;
243
244             }
245             catch (Exception e)
246             {
247                 throw new TransactionConstructorException("Deserialization failed", e);
248             }
249         }
250
251         public bool IsCoinBase
252         {
253             get { return (vin.Length == 1 && vin[0].prevout.IsNull && vout.Length >= 1); }
254         }
255
256         public bool IsCoinStake
257         {
258             get
259             {
260                 return (vin.Length > 0 && (!vin[0].prevout.IsNull) && vout.Length >= 2 && vout[0].IsEmpty);
261             }
262         }
263
264         /// <summary>
265         /// Transaction hash
266         /// </summary>
267         public Hash256 Hash
268         {
269             get { return Hash256.Compute256(this); }
270         }
271
272         /// <summary>
273         /// A sequence of bytes, which corresponds to the current state of CTransaction.
274         /// </summary>
275         public static implicit operator byte[] (CTransaction tx)
276         {
277             var resultBytes = new List<byte>();
278
279             resultBytes.AddRange(BitConverter.GetBytes(tx.nVersion));
280             resultBytes.AddRange(BitConverter.GetBytes(tx.nTime));
281             resultBytes.AddRange(VarInt.EncodeVarInt(tx.vin.LongLength));
282
283             foreach (var input in tx.vin)
284             {
285                 resultBytes.AddRange((byte[])input);
286             }
287
288             resultBytes.AddRange(VarInt.EncodeVarInt(tx.vout.LongLength));
289
290             foreach (var output in tx.vout)
291             {
292                 resultBytes.AddRange((byte[])output);
293             }
294
295             resultBytes.AddRange(BitConverter.GetBytes(tx.nLockTime));
296
297             return resultBytes.ToArray();
298         }
299
300         public override string ToString()
301         {
302             var sb = new StringBuilder();
303
304             sb.AppendFormat("CTransaction(\n nVersion={0},\n nTime={1},\n", nVersion, nTime);
305
306             foreach (var txin in vin)
307             {
308                 sb.AppendFormat(" {0},\n", txin.ToString());
309             }
310
311             foreach (var txout in vout)
312             {
313                 sb.AppendFormat(" {0},\n", txout.ToString());
314             }
315
316             sb.AppendFormat("\nnLockTime={0}\n)", nLockTime);
317
318             return sb.ToString();
319         }
320         }
321 }