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