X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=Novacoin%2FCBlockStore.cs;h=a4466abf6dc697e8ae4bad0a0abaac04304af89b;hb=bbc180adad56adde0fd4421c9b8b49bca27397a4;hp=c6361218e6bb0ff4b2809d92c5ecd7aef440d685;hpb=514b68fc3103fca11d6d06ddb65ed8d2a14507e1;p=NovacoinLibrary.git
diff --git a/Novacoin/CBlockStore.cs b/Novacoin/CBlockStore.cs
index c636121..a4466ab 100644
--- a/Novacoin/CBlockStore.cs
+++ b/Novacoin/CBlockStore.cs
@@ -124,8 +124,14 @@ namespace Novacoin
///
/// Block height, encoded in VarInt format
///
- [Column("Height")]
- public byte[] Height { get; set; }
+ [Column("nHeight")]
+ public uint nHeight { get; set; }
+
+ ///
+ /// Chain trust score, serialized and trimmed uint256 representation.
+ ///
+ [Column("ChainTrust")]
+ public byte[] ChainTrust { get; set; }
///
/// Block position in file, encoded in VarInt format
@@ -161,17 +167,6 @@ namespace Novacoin
}
///
- /// Accessor and mutator for Height value.
- ///
- [Ignore]
- public uint nHeight
- {
- get { return (uint)VarInt.DecodeVarInt(Height); }
- set { Height = VarInt.EncodeVarInt(value); }
- }
-
-
- ///
/// Fill database item with data from given block header.
///
/// Block header
@@ -509,7 +504,11 @@ namespace Novacoin
///
/// Chain trust score
///
- public uint256 nChainTrust;
+ [Ignore]
+ public uint256 nChainTrust {
+ get { return Interop.AppendWithZeros(ChainTrust); }
+ set { ChainTrust = Interop.TrimArray(value); }
+ }
}
///
@@ -707,6 +706,11 @@ namespace Novacoin
private SQLiteConnection dbConn;
///
+ /// Current SQLite platform
+ ///
+ private ISQLitePlatform dbPlatform;
+
+ ///
/// Block file.
///
private string strBlockFile;
@@ -774,6 +778,9 @@ namespace Novacoin
/// Block file stream with read/write access
///
private Stream fStreamReadWrite;
+ private uint nBestHeight;
+ private uint nTimeBestReceived;
+ private int nTransactionsUpdated;
///
/// Init the block storage manager.
@@ -786,7 +793,8 @@ namespace Novacoin
strBlockFile = BlockFile;
bool firstInit = !File.Exists(strDbFile);
- dbConn = new SQLiteConnection(new SQLitePlatformGeneric(), strDbFile);
+ dbPlatform = new SQLitePlatformGeneric();
+ dbConn = new SQLiteConnection(dbPlatform, strDbFile);
fStreamReadWrite = File.Open(strBlockFile, FileMode.OpenOrCreate, FileAccess.ReadWrite);
@@ -873,14 +881,24 @@ namespace Novacoin
return false;
}
- public bool FetchInputs(ref CTransaction tx, ref Dictionary 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 queued, ref Dictionary 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 +908,71 @@ namespace Novacoin
));
}
- var queryResults = dbConn.Query(queryBuilder.ToString());
+ var queryResults = dbConn.Query(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;
}
@@ -971,23 +1033,27 @@ namespace Novacoin
return false;
}
- if (dbConn.Insert(itemTemplate) == 0 || !blockMap.TryAdd(blockHash, itemTemplate))
+ if (dbConn.Insert(itemTemplate) == 0)
{
- return false;
+ return false; // Insert failed
+ }
+
+ // Get last RowID.
+ itemTemplate.ItemID = dbPlatform.SQLiteApi.LastInsertRowid(dbConn.Handle);
+
+ if (!blockMap.TryAdd(blockHash, itemTemplate))
+ {
+ return false; // blockMap add failed
}
if (itemTemplate.nChainTrust > nBestChainTrust)
{
// New best chain
- // TODO: SetBestChain implementation
-
- /*
if (!SetBestChain(ref itemTemplate))
{
return false; // SetBestChain failed.
}
- */
}
return true;
@@ -995,8 +1061,6 @@ namespace Novacoin
private bool SetBestChain(ref CBlockStoreItem cursor)
{
- dbConn.BeginTransaction();
-
uint256 hashBlock = cursor.Hash;
if (genesisBlockCursor == null && hashBlock == NetUtils.nHashGenesisBlock)
@@ -1013,10 +1077,10 @@ namespace Novacoin
else
{
// the first block in the new chain that will cause it to become the new best chain
- CBlockStoreItem cursorIntermediate = cursor;
+ var cursorIntermediate = cursor;
// list of blocks that need to be connected afterwards
- List secondary = new List();
+ var secondary = new List();
// Reorganize is costly in terms of db load, as it works in a single db transaction.
// Try to limit how much needs to be done inside
@@ -1029,16 +1093,36 @@ namespace Novacoin
// Switch to new best branch
if (!Reorganize(cursorIntermediate))
{
- dbConn.Rollback();
InvalidChainFound(cursor);
return false; // reorganize failed
}
+ // Connect further blocks
+ foreach (var currentCursor in secondary)
+ {
+ CBlock block;
+ if (!currentCursor.ReadFromFile(ref fStreamReadWrite, out block))
+ {
+ // ReadFromDisk failed
+ break;
+ }
+ // errors now are not fatal, we still did a reorganisation to a new chain in a valid way
+ if (!SetBestChainInner(currentCursor))
+ {
+ break;
+ }
+ }
}
+ nHashBestChain = cursor.Hash;
+ bestBlockCursor = cursor;
+ nBestHeight = cursor.nHeight;
+ nBestChainTrust = cursor.nChainTrust;
+ nTimeBestReceived = Interop.GetTime();
+ nTransactionsUpdated++;
- throw new NotImplementedException();
+ return true;
}
private void InvalidChainFound(CBlockStoreItem cursor)
@@ -1048,6 +1132,120 @@ namespace Novacoin
private bool Reorganize(CBlockStoreItem cursorIntermediate)
{
+ // Find the fork
+ var fork = bestBlockCursor;
+ var longer = cursorIntermediate;
+
+ while (fork.ItemID != longer.ItemID)
+ {
+ while (longer.nHeight > fork.nHeight)
+ {
+ if ((longer = longer.prev) == null)
+ {
+ return false; // longer.prev is null
+ }
+ }
+
+ if (fork.ItemID == longer.ItemID)
+ {
+ break;
+ }
+
+ if ((fork = fork.prev) == null)
+ {
+ return false; // fork.prev is null
+ }
+ }
+
+ // List of what to disconnect
+ var disconnect = new List();
+ for (var cursor = bestBlockCursor; cursor.ItemID != fork.ItemID; cursor = cursor.prev)
+ {
+ disconnect.Add(cursor);
+ }
+
+ // List of what to connect
+ var connect = new List();
+ for (var cursor = cursorIntermediate; cursor.ItemID != fork.ItemID; cursor = cursor.prev)
+ {
+ connect.Add(cursor);
+ }
+ connect.Reverse();
+
+ // Disconnect shorter branch
+ var txResurrect = new List();
+ foreach (var blockCursor in disconnect)
+ {
+ CBlock block;
+ if (!blockCursor.ReadFromFile(ref fStreamReadWrite, out block))
+ {
+ return false; // ReadFromFile for disconnect failed.
+ }
+ if (!DisconnectBlock(blockCursor, ref block))
+ {
+ return false; // DisconnectBlock failed.
+ }
+
+ // Queue memory transactions to resurrect
+ foreach (var tx in block.vtx)
+ {
+ if (!tx.IsCoinBase && !tx.IsCoinStake)
+ {
+ txResurrect.Add(tx);
+ }
+ }
+ }
+
+
+ // Connect longer branch
+ var txDelete = new List();
+ foreach (var cursor in connect)
+ {
+ CBlock block;
+ if (!cursor.ReadFromFile(ref fStreamReadWrite, out block))
+ {
+ return false; // ReadFromDisk for connect failed
+ }
+
+ if (!ConnectBlock(cursor, ref block))
+ {
+ // Invalid block
+ return false; // ConnectBlock failed
+ }
+
+ // Queue memory transactions to delete
+ foreach (var tx in block.vtx)
+ {
+ txDelete.Add(tx);
+ }
+ }
+
+ if (!WriteHashBestChain(cursorIntermediate.Hash))
+ {
+ return false; // WriteHashBestChain failed
+ }
+
+ // Make sure it's successfully written to disk
+ dbConn.Commit();
+
+ // Resurrect memory transactions that were in the disconnected branch
+ foreach (var tx in txResurrect)
+ {
+ mapUnconfirmedTx.TryAdd(tx.Hash, tx);
+ }
+
+ // Delete redundant memory transactions that are in the connected branch
+ foreach (var tx in txDelete)
+ {
+ CTransaction dummy;
+ mapUnconfirmedTx.TryRemove(tx.Hash, out dummy);
+ }
+
+ return true; // Done
+ }
+
+ private bool DisconnectBlock(CBlockStoreItem blockCursor, ref CBlock block)
+ {
throw new NotImplementedException();
}
@@ -1055,11 +1253,14 @@ namespace Novacoin
{
uint256 hash = cursor.Hash;
CBlock block;
+ if (!cursor.ReadFromFile(ref fStreamReadWrite, out block))
+ {
+ return false; // Unable to read block from file.
+ }
// Adding to current best branch
- if (!ConnectBlock(cursor, false, out block) || !WriteHashBestChain(hash))
+ if (!ConnectBlock(cursor, ref block) || !WriteHashBestChain(hash))
{
- dbConn.Rollback();
InvalidChainFound(cursor);
return false;
}
@@ -1079,14 +1280,8 @@ namespace Novacoin
return true;
}
- private bool ConnectBlock(CBlockStoreItem cursor, bool fJustCheck, out CBlock block)
+ private bool ConnectBlock(CBlockStoreItem cursor, ref CBlock block, bool fJustCheck=false)
{
- var reader = new BinaryReader(fStreamReadWrite).BaseStream;
- if (cursor.ReadFromFile(ref reader, out block))
- {
- return false; // Unable to read block from file.
- }
-
// Check it again in case a previous version let a bad block in, but skip BlockSig checking
if (!block.CheckBlock(!fJustCheck, !fJustCheck, false))
{
@@ -1394,8 +1589,6 @@ namespace Novacoin
readerForBlocks.Seek(nOffset, SeekOrigin.Begin); // Seek to previous offset + previous block length
- dbConn.BeginTransaction();
-
while (readerForBlocks.Read(buffer, 0, 4) == 4) // Read magic number
{
var nMagic = BitConverter.ToUInt32(buffer, 0);
@@ -1437,12 +1630,13 @@ namespace Novacoin
int nCount = blockMap.Count;
Console.WriteLine("nCount={0}, Hash={1}, Time={2}", nCount, block.header.Hash, DateTime.Now); // Commit on each 100th block
+ /*
if (nCount % 100 == 0 && nCount != 0)
{
Console.WriteLine("Commit...");
dbConn.Commit();
dbConn.BeginTransaction();
- }
+ }*/
}
dbConn.Commit();