Use byte[] instead of IEnumerable<byte> if possible
[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 = 1;
34
35         /// <summary>
36         /// Transaction timestamp.
37         /// </summary>
38         public uint nTime = 0;
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 = 0;
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             vin = new CTxIn[0];
64             vout = new CTxOut[0];
65         }
66
67         /// <summary>
68         /// Initialize new instance as a copy of another transaction
69         /// </summary>
70         /// <param name="tx">Transaction to copy from</param>
71         public CTransaction(CTransaction tx)
72         {
73             nVersion = tx.nVersion;
74             nTime = tx.nTime;
75
76             vin = new CTxIn[tx.vin.Length];
77
78             for (int i = 0; i < vin.Length; i++)
79             {
80                 vin[i] = new CTxIn(tx.vin[i]);
81             }
82
83             vout = new CTxOut[tx.vout.Length];
84
85             for (int i = 0; i < vout.Length; i++)
86             {
87                 vout[i] = new CTxOut(tx.vout[i]);
88             }
89
90             nLockTime = tx.nLockTime;
91         }
92
93
94         /// <summary>
95         /// Parse byte sequence and initialize new instance of CTransaction
96         /// </summary>
97         /// <param name="txBytes">Byte sequence</param>
98                 public CTransaction(IList<byte> txBytes)
99         {
100             var wBytes = new ByteQueue(txBytes);
101
102             nVersion = BitConverter.ToUInt32(wBytes.Get(4), 0);
103             nTime = BitConverter.ToUInt32(wBytes.Get(4), 0);
104
105             int nInputs = (int)(int)wBytes.GetVarInt();
106             vin = new CTxIn[nInputs];
107
108             for (int nCurrentInput = 0; nCurrentInput < nInputs; nCurrentInput++)
109             {
110                 // Fill inputs array
111                 vin[nCurrentInput] = new CTxIn();
112                 
113                 vin[nCurrentInput].prevout = new COutPoint(wBytes.Get(36));
114
115                 int nScriptSigLen = (int)wBytes.GetVarInt();
116                 vin[nCurrentInput].scriptSig = new CScript(wBytes.Get(nScriptSigLen));
117
118                 vin[nCurrentInput].nSequence = BitConverter.ToUInt32(wBytes.Get(4), 0);
119             }
120
121             int nOutputs = (int)wBytes.GetVarInt();
122             vout = new CTxOut[nOutputs];
123
124             for (int nCurrentOutput = 0; nCurrentOutput < nOutputs; nCurrentOutput++)
125             {
126                 // Fill outputs array
127                 vout[nCurrentOutput] = new CTxOut();
128                 vout[nCurrentOutput].nValue = BitConverter.ToInt64(wBytes.Get(8), 0);
129
130                 int nScriptPKLen = (int)wBytes.GetVarInt();
131                 vout[nCurrentOutput].scriptPubKey = new CScript(wBytes.Get(nScriptPKLen));
132             }
133
134             nLockTime = BitConverter.ToUInt32(wBytes.Get(4), 0);
135         }
136
137         /// <summary>
138         /// Read transactions array which is encoded in the block body.
139         /// </summary>
140         /// <param name="wTxBytes">Bytes sequence</param>
141         /// <returns>Transactions array</returns>
142         public static CTransaction[] ReadTransactionsList(ref ByteQueue wTxBytes)
143         {
144             // Read amount of transactions
145             int nTransactions = (int)wTxBytes.GetVarInt();
146             var tx = new CTransaction[nTransactions];
147
148             for (int nTx = 0; nTx < nTransactions; nTx++)
149             {
150                 // Fill the transactions array
151                 tx[nTx] = new CTransaction();
152
153                 tx[nTx].nVersion = BitConverter.ToUInt32(wTxBytes.Get(4), 0);
154                 tx[nTx].nTime = BitConverter.ToUInt32(wTxBytes.Get(4), 0);
155
156                 // Inputs array
157                 tx[nTx].vin = CTxIn.ReadTxInList(ref wTxBytes);
158
159                 // outputs array
160                 tx[nTx].vout = CTxOut.ReadTxOutList(ref wTxBytes);
161
162                 tx[nTx].nLockTime = BitConverter.ToUInt32(wTxBytes.Get(4), 0);
163             }
164
165             return tx;
166         }
167
168         public bool IsCoinBase
169         {
170             get { return (vin.Length == 1 && vin[0].prevout.IsNull && vout.Length >= 1); }
171         }
172
173         public bool IsCoinStake
174         {
175             get
176             {
177                 return (vin.Length > 0 && (!vin[0].prevout.IsNull) && vout.Length >= 2 && vout[0].IsEmpty);
178             }
179         }
180
181         /// <summary>
182         /// Transaction hash
183         /// </summary>
184         public Hash256 Hash
185         {
186             get { return Hash256.Compute256(Bytes); }
187         }
188
189         /// <summary>
190         /// A sequence of bytes, which corresponds to the current state of CTransaction.
191         /// </summary>
192         public IList<byte> Bytes
193         {
194             get
195             {
196                 var resultBytes = new List<byte>();
197
198                 // Typical transaction example:
199                 //
200                 // 01000000 -- version
201                 // 78b4c953 -- timestamp
202                 // 06       -- amount of txins
203                 // 340d96b77ec4ee9d42b31cadc2fab911e48d48c36274d516f226d5e85bbc512c -- txin hash
204                 // 01000000 -- txin outnumber
205                 // 6b       -- txin scriptSig length
206                 // 483045022100c8df1fc17b6ea1355a39b92146ec67b3b53565e636e028010d3a8a87f6f805f202203888b9b74df03c3960773f2a81b2dfd1efb08bb036a8f3600bd24d5ed694cd5a0121030dd13e6d3c63fa10cc0b6bf968fbbfcb9a988b333813b1f22d04fa60e344bc4c -- txin scriptSig
207                 // ffffffff -- txin nSequence
208                 // 364c640420de8fa77313475970bf09ce4d0b1f8eabb8f1d6ea49d90c85b202ee -- txin hash
209                 // 01000000 -- txin outnumber
210                 // 6b       -- txin scriptSig length
211                 // 483045022100b651bf3a6835d714d2c990c742136d769258d0170c9aac24803b986050a8655b0220623651077ff14b0a9d61e30e30f2c15352f70491096f0ec655ae1c79a44e53aa0121030dd13e6d3c63fa10cc0b6bf968fbbfcb9a988b333813b1f22d04fa60e344bc4c -- txin scriptSig
212                 // ffffffff -- txin nSequence
213                 // 7adbd5f2e521f567bfea2cb63e65d55e66c83563fe253464b75184a5e462043d -- txin hash
214                 // 00000000 -- txin outnumber
215                 // 6a       -- txin scriptSig length
216                 // 4730440220183609f2b995993acc9df241aff722d48b9a731b0cd376212934565723ed81f00220737e7ce75ef39bdc061d0dcdba3ee24e43b899696a7c96803cee0a79e1f78ecb0121030dd13e6d3c63fa10cc0b6bf968fbbfcb9a988b333813b1f22d04fa60e344bc4c -- txin scriptSig
217                 // ffffffff -- txin nSequence
218                 // 999eb03e00a41c2f9fde8865a554ceebbc48d30f4c8ba22dd88da8c9b46fa920 -- txin hash
219                 // 03000000 -- txin outnumber
220                 // 6b       -- txin scriptSig length
221                 // 483045022100ec1ab104ef086ba79b0f2611ebf1bfdd22a7a1020f6630fa1c6707546626e0db022056093d4048a999392185ccc735ef736a5497bd68f60b42e6c0c93ba770b54d010121030dd13e6d3c63fa10cc0b6bf968fbbfcb9a988b333813b1f22d04fa60e344bc4c -- txin scriptSig
222                 // ffffffff -- txin nSequence
223                 // c0543b86be257ddd85b014a76718a70fab9eaa3c477460e4ca187094d86f369c -- txin hash
224                 // 05000000 -- txin outnumber
225                 // 69       -- txin scriptSig length
226                 // 463043021f24275c72f952043174daf01d7f713f878625f0522124a3cab48a0a2e12604202201b47742e6697b0ebdd1e4ba49c74baf142a0228ad0e0ee847488994c9dce78470121030dd13e6d3c63fa10cc0b6bf968fbbfcb9a988b333813b1f22d04fa60e344bc4c -- txin scriptSig
227                 // ffffffff -- txin nSequence
228                 // e1793d4519147782293dd1db6d90e461265d91db2cc6889c37209394d42ad10d -- txin hash
229                 // 05000000 -- txin outnumber
230                 // 6a       -- txin scriptSig length
231                 // 473044022018a0c3d73b2765d75380614ab36ee8e3c937080894a19166128b1e3357b208fb0220233c9609985f535547381431526867ad0255ec4969afe5c360544992ed6b3ed60121030dd13e6d3c63fa10cc0b6bf968fbbfcb9a988b333813b1f22d04fa60e344bc4c -- txin scriptSig
232                 // ffffffff -- txin nSequence
233                 // 02 -- amount of txouts
234                 // e542000000000000 -- txout value
235                 // 19 -- scriptPubKey length
236                 // 76a91457d84c814b14bd86bf32f106b733baa693db7dc788ac -- scriptPubKey
237                 // 409c000000000000 -- txout value
238                 // 19 -- scriptPubKey length
239                 // 76a91408c8768d5d6bf7c1d9609da4e766c3f1752247b188ac -- scriptPubKey
240                 // 00000000 -- lock time
241
242                 resultBytes.AddRange(BitConverter.GetBytes(nVersion));
243                 resultBytes.AddRange(BitConverter.GetBytes(nTime));
244                 resultBytes.AddRange(VarInt.EncodeVarInt(vin.LongLength));
245
246                 foreach (var input in vin)
247                 {
248                     resultBytes.AddRange(input.Bytes);
249                 }
250
251                 resultBytes.AddRange(VarInt.EncodeVarInt(vout.LongLength));
252
253                 foreach (var output in vout)
254                 {
255                     resultBytes.AddRange(output.Bytes);
256                 }
257
258                 resultBytes.AddRange(BitConverter.GetBytes(nLockTime));
259
260                 return resultBytes;
261             }
262         }
263
264         public override string ToString()
265         {
266             var sb = new StringBuilder();
267
268             sb.AppendFormat("CTransaction(\n nVersion={0},\n nTime={1},\n", nVersion, nTime);
269
270             foreach (var txin in vin)
271             {
272                 sb.AppendFormat(" {0},\n", txin.ToString());
273             }
274
275             foreach (var txout in vout)
276             {
277                 sb.AppendFormat(" {0},\n", txout.ToString());
278             }
279
280             sb.AppendFormat("\nnLockTime={0}\n)", nLockTime);
281
282             return sb.ToString();
283         }
284         }
285 }