Remove legacy stake reward calculation code.
[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 using System.IO;
23 using System.Diagnostics.Contracts;
24 using System.Numerics;
25
26 namespace Novacoin
27 {
28     [Serializable]
29     public class TransactionConstructorException : Exception
30     {
31         public TransactionConstructorException()
32         {
33         }
34
35         public TransactionConstructorException(string message)
36                 : base(message)
37         {
38         }
39
40         public TransactionConstructorException(string message, Exception inner)
41                 : base(message, inner)
42         {
43         }
44     }
45
46     /// <summary>
47     /// Represents the transaction.
48     /// </summary>
49     public class CTransaction
50     {
51         /// <summary>
52         /// One cent = 10000 satoshis.
53         /// </summary>
54         public const long nCent = 10000;
55
56         /// <summary>
57         /// One coin = 1000000 satoshis.
58         /// </summary>
59         public const long nCoin = 1000000;
60         
61         /// <summary>
62         /// Sanity checking threshold.
63         /// </summary>
64         public const long nMaxMoney = 2000000000 * nCoin;
65
66         public const long nMinTxFee = nCent / 10;
67         public const long nMinRelayTxFee = nCent / 50;
68         public const long nMinTxoutAmount = nCent / 100;        
69
70         /// <summary>
71         /// Maximum transaction size is 250Kb
72         /// </summary>
73         public const uint nMaxTxSize = 250000;
74
75         public enum MinFeeMode
76         {
77             GMF_BLOCK,
78             GMF_RELAY,
79             GMF_SEND,
80         }
81
82         /// <summary>
83         /// Version of transaction schema.
84         /// </summary>
85         public uint nVersion;
86
87         /// <summary>
88         /// Transaction timestamp.
89         /// </summary>
90         public uint nTime;
91
92         /// <summary>
93         /// Array of transaction inputs
94         /// </summary>
95         public CTxIn[] vin;
96
97         /// <summary>
98         /// Array of transaction outputs
99         /// </summary>
100         public CTxOut[] vout;
101
102         /// <summary>
103         /// Block height or timestamp when transaction is final
104         /// </summary>
105         public uint nLockTime;
106
107         /// <summary>
108         /// Initialize an empty instance
109         /// </summary>
110         public CTransaction()
111         {
112             // Initialize empty input and output arrays. Please note that such 
113             // configuration is not valid for real transaction, you have to supply 
114             // at least one input and one output.
115             nVersion = 1;
116             nTime = 0;
117             vin = new CTxIn[0];
118             vout = new CTxOut[0];
119             nLockTime = 0;
120         }
121
122         /// <summary>
123         /// Initialize new instance as a copy of another transaction
124         /// </summary>
125         /// <param name="tx">Transaction to copy from</param>
126         public CTransaction(CTransaction tx)
127         {
128             nVersion = tx.nVersion;
129             nTime = tx.nTime;
130
131             vin = new CTxIn[tx.vin.Length];
132
133             for (int i = 0; i < vin.Length; i++)
134             {
135                 vin[i] = new CTxIn(tx.vin[i]);
136             }
137
138             vout = new CTxOut[tx.vout.Length];
139
140             for (int i = 0; i < vout.Length; i++)
141             {
142                 vout[i] = new CTxOut(tx.vout[i]);
143             }
144
145             nLockTime = tx.nLockTime;
146         }
147
148         /// <summary>
149         /// Attempts to execute all transaction scripts and validate the results.
150         /// </summary>
151         /// <returns>Checking result.</returns>
152         public bool VerifyScripts()
153         {
154             if (IsCoinBase)
155             {
156                 return true;
157             }
158
159             for (int i = 0; i < vin.Length; i++)
160             {
161                 var outpoint = vin[i].prevout;
162
163                 TxOutItem txOutCursor;
164                 if (!CBlockStore.Instance.GetTxOutCursor(outpoint, out txOutCursor))
165                     return false;
166
167                 if (!ScriptCode.VerifyScript(vin[i].scriptSig, txOutCursor.scriptPubKey, this, i, (int)scriptflag.SCRIPT_VERIFY_P2SH, 0))
168                     return false;
169             }
170
171             return true;
172         }
173
174         /// <summary>
175         /// Calculate amount of signature operations without trying to properly evaluate P2SH scripts.
176         /// </summary>
177         public uint LegacySigOpCount
178         {
179             get
180             {
181                 uint nSigOps = 0;
182
183                 if (!IsCoinBase)
184                 {
185                     // http://lists.linuxfoundation.org/pipermail/bitcoin-dev/2012-July/001718.html
186
187                     foreach (var txin in vin)
188                     {
189                         nSigOps += txin.scriptSig.GetSigOpCount(false);
190                     }
191                 }
192
193                 foreach (var txout in vout)
194                 {
195                     nSigOps += txout.scriptPubKey.GetSigOpCount(false);
196                 }
197
198                 return nSigOps;
199             }
200         }
201
202         /// <summary>
203         /// Basic sanity checkings
204         /// </summary>
205         /// <returns>Checking result</returns>
206         public bool CheckTransaction()
207         {
208             if (Size > nMaxTxSize || vin.Length == 0 || vout.Length == 0)
209             {
210                 return false;
211             }
212
213             // Check for empty or overflow output values
214             long nValueOut = 0;
215             for (int i = 0; i < vout.Length; i++)
216             {
217                 CTxOut txout = vout[i];
218                 if (txout.IsEmpty && !IsCoinBase && !IsCoinStake)
219                 {
220                     // Empty outputs aren't allowed for user transactions.
221                     return false;
222                 }
223
224                 nValueOut += txout.nValue;
225                 if (!MoneyRange(nValueOut))
226                 {
227                     return false;
228                 }
229             }
230
231             // Check for duplicate inputs
232             var InOutPoints = new List<COutPoint>();
233             foreach (var txin in vin)
234             {
235                 if (InOutPoints.IndexOf(txin.prevout) != -1)
236                 {
237                     // Duplicate input.
238                     return false;
239                 }
240                 InOutPoints.Add(txin.prevout);
241             }
242
243             if (IsCoinBase)
244             {
245                 if (vin[0].scriptSig.Size < 2 || vin[0].scriptSig.Size > 100)
246                 {
247                     // Script size is invalid
248                     return false;
249                 }
250             }
251             else
252             {
253                 foreach (var txin in vin)
254                 {
255                     if (txin.prevout.IsNull)
256                     {
257                         // Null input in non-coinbase transaction.
258                         return false;
259                     }
260                 }
261             }
262
263             return true;
264         }
265
266         public bool IsFinal(uint nBlockHeight = 0, uint nBlockTime = 0)
267         {
268             // Time based nLockTime
269             if (nLockTime == 0)
270             {
271                 return true;
272             }
273             if (nBlockHeight == 0)
274             {
275                 nBlockHeight = uint.MaxValue; // TODO: stupid stub here, should be best height instead.
276             }
277             if (nBlockTime == 0)
278             {
279                 nBlockTime = NetInfo.GetAdjustedTime();
280             }
281             if (nLockTime < (nLockTime < NetInfo.nLockTimeThreshold ? nBlockHeight : nBlockTime))
282             {
283                 return true;
284             }
285             foreach (var txin in vin)
286             {
287                 if (!txin.IsFinal)
288                 {
289                     return false;
290                 }
291             }
292             return true;
293         }
294
295         /// <summary>
296         /// Parse byte sequence and initialize new instance of CTransaction
297         /// </summary>
298         /// <param name="txBytes">Byte sequence</param>
299         public CTransaction(byte[] txBytes)
300         {
301             try
302             {
303                 var stream = new MemoryStream(txBytes);
304                 var reader = new BinaryReader(stream);
305
306                 nVersion = reader.ReadUInt32();
307                 nTime = reader.ReadUInt32();
308
309                 int nInputs = (int)VarInt.ReadVarInt(ref reader);
310                 vin = new CTxIn[nInputs];
311
312                 for (int nCurrentInput = 0; nCurrentInput < nInputs; nCurrentInput++)
313                 {
314                     // Fill inputs array
315                     vin[nCurrentInput] = new CTxIn();
316
317                     vin[nCurrentInput].prevout = new COutPoint(reader.ReadBytes(36));
318
319                     int nScriptSigLen = (int)VarInt.ReadVarInt(ref reader);
320                     vin[nCurrentInput].scriptSig = new CScript(reader.ReadBytes(nScriptSigLen));
321
322                     vin[nCurrentInput].nSequence = reader.ReadUInt32();
323                 }
324
325                 int nOutputs = (int)VarInt.ReadVarInt(ref reader);
326                 vout = new CTxOut[nOutputs];
327
328                 for (int nCurrentOutput = 0; nCurrentOutput < nOutputs; nCurrentOutput++)
329                 {
330                     // Fill outputs array
331                     vout[nCurrentOutput] = new CTxOut();
332                     vout[nCurrentOutput].nValue = reader.ReadInt64();
333
334                     int nScriptPKLen = (int)VarInt.ReadVarInt(ref reader);
335                     vout[nCurrentOutput].scriptPubKey = new CScript(reader.ReadBytes(nScriptPKLen));
336                 }
337
338                 nLockTime = reader.ReadUInt32();
339             }
340             catch (Exception e)
341             {
342                 throw new TransactionConstructorException("Deserialization failed", e);
343             }
344         }
345
346         /// <summary>
347         /// Serialized size
348         /// </summary>
349         public uint Size
350         {
351             get
352             {
353                 uint nSize = 12; // nVersion, nTime, nLockLime
354
355                 nSize += VarInt.GetEncodedSize(vin.Length);
356                 nSize += VarInt.GetEncodedSize(vout.Length);
357
358                 foreach (var input in vin)
359                 {
360                     nSize += input.Size;
361                 }
362
363                 foreach (var output in vout)
364                 {
365                     nSize += output.Size;
366                 }
367
368                 return nSize;
369             }
370         }
371
372         /// <summary>
373         /// Read transactions array which is encoded in the block body.
374         /// </summary>
375         /// <param name="wTxBytes">Bytes sequence</param>
376         /// <returns>Transactions array</returns>
377         internal static CTransaction[] ReadTransactionsList(ref BinaryReader reader)
378         {
379             try
380             {
381                 // Read amount of transactions
382                 int nTransactions = (int)VarInt.ReadVarInt(ref reader);
383                 var tx = new CTransaction[nTransactions];
384
385                 for (int nTx = 0; nTx < nTransactions; nTx++)
386                 {
387                     // Fill the transactions array
388                     tx[nTx] = new CTransaction();
389
390                     tx[nTx].nVersion = reader.ReadUInt32();
391                     tx[nTx].nTime = reader.ReadUInt32();
392
393                     // Inputs array
394                     tx[nTx].vin = CTxIn.ReadTxInList(ref reader);
395
396                     // outputs array
397                     tx[nTx].vout = CTxOut.ReadTxOutList(ref reader);
398
399                     tx[nTx].nLockTime = reader.ReadUInt32();
400                 }
401
402                 return tx;
403
404             }
405             catch (Exception e)
406             {
407                 throw new TransactionConstructorException("Deserialization failed", e);
408             }
409         }
410
411         public bool IsCoinBase
412         {
413             get { return (vin.Length == 1 && vin[0].prevout.IsNull && vout.Length >= 1); }
414         }
415
416         public bool IsCoinStake
417         {
418             get
419             {
420                 return (vin.Length > 0 && (!vin[0].prevout.IsNull) && vout.Length >= 2 && vout[0].IsEmpty);
421             }
422         }
423
424         /// <summary>
425         /// Transaction hash
426         /// </summary>
427         public uint256 Hash
428         {
429             get { return CryptoUtils.ComputeHash256(this); }
430         }
431
432         /// <summary>
433         /// Amount of novacoins spent by this transaction.
434         /// </summary>
435         public long nValueOut
436         {
437             get
438             {
439                 long nValueOut = 0;
440                 foreach (var txout in vout)
441                 {
442                     nValueOut += txout.nValue;
443                     Contract.Assert(MoneyRange(txout.nValue) && MoneyRange(nValueOut));
444                 }
445                 return nValueOut;
446             }
447         }
448
449         /// <summary>
450         /// A sequence of bytes, which corresponds to the current state of CTransaction.
451         /// </summary>
452         public static implicit operator byte[] (CTransaction tx)
453         {
454             var stream = new MemoryStream();
455             var writer = new BinaryWriter(stream);
456
457             writer.Write(tx.nVersion);
458             writer.Write(tx.nTime);
459             writer.Write(VarInt.EncodeVarInt(tx.vin.LongLength));
460
461             foreach (var input in tx.vin)
462             {
463                 writer.Write(input);
464             }
465
466             writer.Write(VarInt.EncodeVarInt(tx.vout.LongLength));
467
468             foreach (var output in tx.vout)
469             {
470                 writer.Write(output);
471             }
472
473             writer.Write(tx.nLockTime);
474             var resultBytes = stream.ToArray();
475             writer.Close();
476
477             return resultBytes;
478         }
479
480         public override string ToString()
481         {
482             var sb = new StringBuilder();
483
484             sb.AppendFormat("CTransaction(\n nVersion={0},\n nTime={1},\n", nVersion, nTime);
485
486             foreach (var txin in vin)
487             {
488                 sb.AppendFormat(" {0},\n", txin);
489             }
490
491             foreach (var txout in vout)
492             {
493                 sb.AppendFormat(" {0},\n", txout);
494             }
495
496             sb.AppendFormat("\nnLockTime={0}\n)", nLockTime);
497
498             return sb.ToString();
499         }
500
501         public static bool MoneyRange(long nValue) { return (nValue <= nMaxMoney); }
502
503         /// <summary>
504         /// Get total sigops.
505         /// </summary>
506         /// <param name="inputs">Inputs map.</param>
507         /// <returns>Amount of sigops.</returns>
508         public uint GetP2SHSigOpCount(ref Dictionary<COutPoint, TxOutItem> inputs)
509         {
510             if (IsCoinBase)
511             {
512                 return 0;
513             }
514
515             uint nSigOps = 0;
516             for (var i = 0; i < vin.Length; i++)
517             {
518                 var prevout = GetOutputFor(vin[i], ref inputs);
519                 if (prevout.scriptPubKey.IsPayToScriptHash)
520                 {
521                     nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig);
522                 }
523             }
524
525             return nSigOps;
526         }
527
528         /// <summary>
529         /// Get sum of inputs spent by this transaction.
530         /// </summary>
531         /// <param name="inputs">Reference to innputs map.</param>
532         /// <returns>Sum of inputs.</returns>
533         public long GetValueIn(ref Dictionary<COutPoint, TxOutItem> inputs)
534         {
535             if (IsCoinBase)
536             {
537                 return 0;
538             }
539
540             long nResult = 0;
541             for (int i = 0; i < vin.Length; i++)
542             {
543                 nResult += GetOutputFor(vin[i], ref inputs).nValue;
544             }
545
546             return nResult;
547         }
548
549         /// <summary>
550         /// Helper method to find output in the map.
551         /// </summary>
552         /// <param name="input">Transaction input.</param>
553         /// <param name="inputs">eference to inuts map.</param>
554         /// <returns>Parent output.</returns>
555         private CTxOut GetOutputFor(CTxIn input, ref Dictionary<COutPoint, TxOutItem> inputs)
556         {
557             if (!inputs.ContainsKey(input.prevout))
558             {
559                 throw new Exception("No such input");
560             }
561
562             var outItem = inputs[input.prevout];
563
564             return new CTxOut(outItem.nValue, outItem.scriptPubKey);
565         }
566
567         /// <summary>
568         /// Calculate coin*age. 
569         /// 
570         /// Note, only those coins meeting minimum age requirement counts.
571         /// </summary>
572         /// <param name="inputs">Inputs set.</param>
573         /// <param name="nCoinAge">Coin age calculation result.</param>
574         /// <returns>Result</returns>
575         public bool GetCoinAge(ref Dictionary<COutPoint, TxOutItem> inputs, out long nCoinAge)
576         {
577             BigInteger bnCentSecond = 0;  // coin age in the unit of cent-seconds
578             nCoinAge = 0;
579
580             if (IsCoinBase)
581             {
582                 // Nothing spent by coinbase, coinage is always zero.
583                 return true;
584             }
585
586             for( var i = 0; i<vin.Length; i++)
587             {
588                 var prevout = vin[i].prevout;
589                 Contract.Assert(inputs.ContainsKey(prevout));
590                 var input = inputs[prevout];
591
592                 CBlockStoreItem parentBlockCursor;
593                 var merkleItem = CBlockStore.Instance.GetMerkleCursor(input, out parentBlockCursor);
594
595                 if (merkleItem == null)
596                 {
597                     return false; // Unable to find merkle node
598                 }
599
600                 if (nTime < merkleItem.nTime)
601                 {
602                     return false;  // Transaction timestamp violation
603                 }
604
605                 if (parentBlockCursor.nTime + StakeModifier.nStakeMinAge > nTime)
606                 {
607                     continue; // only count coins meeting min age requirement
608                 }
609
610                 long nValueIn = input.nValue;
611                 bnCentSecond += new BigInteger(nValueIn) * (nTime - merkleItem.nTime) / nCent;
612             }
613
614             BigInteger bnCoinDay = bnCentSecond * nCent / nCoin / (24 * 60 * 60);
615             nCoinAge = (long)bnCoinDay;
616
617             return true;
618         }
619
620         public long GetMinFee(uint nBlockSize, bool fAllowFree, MinFeeMode mode)
621         {
622             long nMinTxFee = CTransaction.nMinTxFee, nMinRelayTxFee = CTransaction.nMinRelayTxFee;
623             uint nBytes = Size;
624
625             if (IsCoinStake)
626             {
627                 // Enforce 0.01 as minimum fee for old approach or coinstake
628                 nMinTxFee = nCent;
629                 nMinRelayTxFee = nCent;
630             }
631
632             // Base fee is either nMinTxFee or nMinRelayTxFee
633             long nBaseFee = (mode == MinFeeMode.GMF_RELAY) ? nMinRelayTxFee : nMinTxFee;
634
635             uint nNewBlockSize = nBlockSize + nBytes;
636             long nMinFee = (1 + (long)nBytes / 1000) * nBaseFee;
637
638             if (fAllowFree)
639             {
640                 if (nBlockSize == 1)
641                 {
642                     // Transactions under 1K are free
643                     if (nBytes < 1000)
644                         nMinFee = 0;
645                 }
646                 else
647                 {
648                     // Free transaction area
649                     if (nNewBlockSize < 27000)
650                         nMinFee = 0;
651                 }
652             }
653
654             // To limit dust spam, require additional MIN_TX_FEE/MIN_RELAY_TX_FEE for
655             //    each non empty output which is less than 0.01
656             //
657             // It's safe to ignore empty outputs here, because these inputs are allowed
658             //     only for coinbase and coinstake transactions.
659             foreach (var txout in vout)
660             {
661                 if (txout.nValue < nCent && !txout.IsEmpty)
662                 {
663                     nMinFee += nBaseFee;
664                 }
665             }
666
667             var nMaxBlockSizeGen = CBlock.nMaxBlockSize / 2;
668
669             // Raise the price as the block approaches full
670             if (nBlockSize != 1 && nNewBlockSize >= nMaxBlockSizeGen / 2)
671             {
672                 if (nNewBlockSize >= nMaxBlockSizeGen)
673                 {
674                     return nMaxMoney;
675                 }
676
677                 nMinFee *= nMaxBlockSizeGen / (nMaxBlockSizeGen - nNewBlockSize);
678             }
679
680             if (!MoneyRange(nMinFee))
681             {
682                 nMinFee = nMaxMoney;
683             }
684
685             return nMinFee;
686         }
687     }
688 }