FetchInputs
authorCryptoManiac <balthazar@yandex.ru>
Sun, 6 Sep 2015 10:58:28 +0000 (13:58 +0300)
committerCryptoManiac <balthazar@yandex.ru>
Sun, 6 Sep 2015 10:58:28 +0000 (13:58 +0300)
Novacoin/CBlockStore.cs
Novacoin/DatabaseInterfaces.cs [new file with mode: 0644]

index c636121..e6d5fce 100644 (file)
@@ -873,14 +873,24 @@ namespace Novacoin
             return false;
         }
 
-        public bool FetchInputs(ref CTransaction tx, ref Dictionary<uint256, TxOutItem> queued, out TxOutItem[] inputs, bool IsBlock, out bool Invalid)
+        interface InputsJoin : ITxOutItem
         {
-            Invalid = true;
-            inputs = null;
+            byte[] TransactionHash { get; set; }
+        }
+
+        public bool FetchInputs(ref CTransaction tx, ref Dictionary<COutPoint, CTxOut> queued, ref Dictionary<COutPoint, CTxOut> inputs, bool IsBlock, out bool Invalid)
+        {
+            Invalid = false;
+
+            if (tx.IsCoinBase)
+            {
+                // Coinbase transactions have no inputs to fetch.
+                return true; 
+            }
 
             StringBuilder queryBuilder = new StringBuilder();
             
-            queryBuilder.Append("select o.* from [Outputs] o left join [MerkleNodes] m on (m.[nMerkleNodeID] = o.[nMerkleNodeID]) where ");
+            queryBuilder.Append("select o.*, m.[TransactionHash] from [Outputs] o left join [MerkleNodes] m on (m.[nMerkleNodeID] = o.[nMerkleNodeID]) where ");
 
             for (var i = 0; i < tx.vin.Length; i++)
             {
@@ -890,27 +900,71 @@ namespace Novacoin
                 ));
             }
 
-            var queryResults = dbConn.Query<TxOutItem>(queryBuilder.ToString());
+            var queryResults = dbConn.Query<InputsJoin>(queryBuilder.ToString());
 
-            if (queryResults.Count < tx.vin.Length)
+            foreach (var item in queryResults)
             {
-                // It seems than some transactions are being spent in the same block.
+                if (item.IsSpent)
+                {
+                    return false; // Already spent
+                }
+
+                var inputsKey =  new COutPoint(item.TransactionHash, item.nOut);
 
+                // Add output data to dictionary
+                inputs[inputsKey] = new CTxOut(item.nValue, item.scriptPubKey);
+            }
+
+            if (queryResults.Count < tx.vin.Length)
+            {
                 if (IsBlock)
                 {
-                    
+                    // It seems that some transactions are being spent in the same block.
+
+                    foreach (var txin in tx.vin)
+                    {
+                        var outPoint = txin.prevout;
+
+                        if (!queued.ContainsKey(outPoint))
+                        {
+                            return false; // No such transaction
+                        }
+
+                        // Add output data to dictionary
+                        inputs[outPoint] = queued[outPoint];
+
+                        // And remove it from queued data
+                        queued.Remove(outPoint);
+                    }
                 }
                 else
                 {
-                    // TODO: use mapUnconfirmed
+                    // Unconfirmed transaction
+
+                    foreach (var txin in tx.vin)
+                    {
+                        var outPoint = txin.prevout;
+                        CTransaction txPrev;
+
+                        if (!mapUnconfirmedTx.TryGetValue(outPoint.hash, out txPrev))
+                        {
+                            return false; // No such transaction
+                        }
+
+                        if (outPoint.n > txPrev.vout.Length)
+                        {
+                            Invalid = true;
+
+                            return false; // nOut is out of range
+                        }
+
+                        inputs[outPoint] = txPrev.vout[outPoint.n];
+                    }
 
                     return false;
                 }
             }
 
-            inputs = queryResults.ToArray();
-            Invalid = false;
-
             return true;
         }
 
diff --git a/Novacoin/DatabaseInterfaces.cs b/Novacoin/DatabaseInterfaces.cs
new file mode 100644 (file)
index 0000000..7405b0d
--- /dev/null
@@ -0,0 +1,184 @@
+\feffusing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using SQLite.Net;
+using SQLite.Net.Attributes;
+using SQLite.Net.Interop;
+using SQLite.Net.Platform.Generic;
+using SQLiteNetExtensions.Attributes;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+
+namespace Novacoin
+{
+    /// <summary>
+    /// Block headers table
+    /// </summary>
+    public interface IBlockStorageItem
+    {
+        /// <summary>
+        /// Item ID in the database
+        /// </summary>
+        long ItemID { get; set; }
+
+        /// <summary>
+        /// PBKDF2+Salsa20 of block hash
+        /// </summary>
+        byte[] Hash { get; set; }
+
+        /// <summary>
+        /// Version of block schema
+        /// </summary>
+        uint nVersion { get; set; }
+
+        /// <summary>
+        /// Previous block hash.
+        /// </summary>
+        byte[] prevHash { get; set; }
+
+        /// <summary>
+        /// Merkle root hash.
+        /// </summary>
+        byte[] merkleRoot { get; set; }
+
+        /// <summary>
+        /// Block timestamp.
+        /// </summary>
+        uint nTime { get; set; }
+
+        /// <summary>
+        /// Compressed difficulty representation.
+        /// </summary>
+        uint nBits { get; set; }
+
+        /// <summary>
+        /// Nonce counter.
+        /// </summary>
+        uint nNonce { get; set; }
+
+        /// <summary>
+        /// Next block hash.
+        /// </summary>
+        byte[] nextHash { get; set; }
+
+        /// <summary>
+        /// Block type flags
+        /// </summary>
+        BlockType BlockTypeFlag { get; set; }
+
+        /// <summary>
+        /// Stake modifier
+        /// </summary>
+        long nStakeModifier { get; set; }
+
+        /// <summary>
+        /// Proof-of-Stake hash
+        /// </summary>
+        byte[] hashProofOfStake { get; set; }
+
+        /// <summary>
+        /// Stake generation outpoint.
+        /// </summary>
+        byte[] prevoutStake { get; set; }
+
+        /// <summary>
+        /// Stake generation time.
+        /// </summary>
+        uint nStakeTime { get; set; }
+
+        /// <summary>
+        /// Block height
+        /// </summary>
+        byte[] Height { get; set; }
+
+        /// <summary>
+        /// Block position in file
+        /// </summary>
+        byte[] BlockPos { get; set; }
+
+        /// <summary>
+        /// Block size in bytes
+        /// </summary>
+        byte[] BlockSize { get; set; }
+    };
+
+    public interface IMerkleNode
+    {
+        /// <summary>
+        /// Node identifier
+        /// </summary>
+        long nMerkleNodeID { get; set; }
+
+        /// <summary>
+        /// Reference to parent block database item.
+        /// </summary>
+        long nParentBlockID { get; set; }
+
+        /// <summary>
+        /// Transaction type flag
+        /// </summary>
+        TxFlags TransactionFlags { get; set; }
+
+        /// <summary>
+        /// Transaction hash
+        /// </summary>
+        byte[] TransactionHash { get; set; }
+
+        /// <summary>
+        /// Transaction offset from the beginning of block header, encoded in VarInt format.
+        /// </summary>
+        byte[] TxOffset { get; set; }
+
+        /// <summary>
+        /// Transaction size, encoded in VarInt format.
+        /// </summary>
+        byte[] TxSize { get; set; }
+    }
+
+    public interface ITxOutItem
+    {
+        /// <summary>
+        /// Reference to transaction item.
+        /// </summary>
+        long nMerkleNodeID { get; set; }
+
+        /// <summary>
+        /// Output flags
+        /// </summary>
+        OutputFlags outputFlags { get; set; }
+
+        /// <summary>
+        /// Output number in VarInt format.
+        /// </summary>
+        byte[] OutputNumber { get; set; }
+
+        /// <summary>
+        /// Output value in VarInt format.
+        /// </summary>
+        byte[] OutputValue { get; set; }
+
+        /// <summary>
+        /// Second half of script which contains spending instructions.
+        /// </summary>
+        byte[] scriptPubKey { get; set; }
+
+        /// <summary>
+        /// Getter for output number.
+        /// </summary>
+        uint nOut { get; }
+
+        /// <summary>
+        /// Getter for output value.
+        /// </summary>
+        ulong nValue { get; }
+
+        /// <summary>
+        /// Getter ans setter for IsSpent flag.
+        /// </summary>
+        bool IsSpent { get; set; }
+    }
+
+}