using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using SQLite.Net;
using SQLite.Net.Attributes;
using SQLite.Net.Interop;
using SQLite.Net.Platform.Generic;
namespace Novacoin
{
public enum BlockType
{
PROOF_OF_WORK,
PROOF_OF_WORK_MODIFIER,
PROOF_OF_STAKE,
PROOF_OF_STAKE_MODIFIER
};
[Table("BlockStorage")]
class CBlockStoreItem
{
///
/// Item ID in the database
///
[PrimaryKey, AutoIncrement]
public int ItemID { get; set; }
///
/// PBKDF2+Salsa20 of block hash
///
[Unique]
public byte[] Hash { get; set; }
///
/// Next block hash
///
public byte[] NextHash { get; set; }
///
/// Serialized representation of block header
///
public byte[] BlockHeader { get; set; }
///
/// Block type flags
///
public BlockType BlockTypeFlag { get; set; }
///
/// Block position in file
///
public long nBlockPos { get; set; }
///
/// Block size in bytes
///
public int nBlockSize { get; set; }
}
///
/// Block chain node
///
public class CChainNode
{
///
/// Block number
///
public int nDepth;
///
/// Block header
///
public CBlockHeader blockHeader;
///
/// Block type flag
///
public BlockType blockType;
///
/// Next block hash
///
public ScryptHash256 hashNextBlock;
}
public class CBlockStore : IDisposable
{
private bool disposed = false;
private object LockObj = new object();
private SQLiteConnection dbConn = null;
private Dictionary blockMap = new Dictionary();
///
/// Init the block storage manager.
///
/// Path to index database
/// Path to block file
public CBlockStore(string IndexDB = "blockstore.dat", string BlockFile = "blk0001.dat")
{
bool firstInit = !File.Exists(IndexDB);
dbConn = new SQLiteConnection(new SQLitePlatformGeneric(), IndexDB);
if (firstInit)
{
lock (LockObj)
{
dbConn.CreateTable(CreateFlags.AutoIncPK);
}
}
else
{
var QueryGet = dbConn.Query("select * from [BlockStorage] order by [ItemId] asc");
foreach (CBlockStoreItem si in QueryGet)
{
blockMap.Add(
new ScryptHash256(si.Hash),
new CChainNode()
{
blockHeader = new CBlockHeader(si.BlockHeader),
blockType = si.BlockTypeFlag
});
}
}
}
public bool ParseBlockFile(string BlockFile = "bootstrap.dat")
{
// TODO: Rewrite completely.
var QueryGet = dbConn.Query("select * from [BlockStorage] order by [ItemId] desc limit 1");
var nOffset = 0L;
if (QueryGet.Count() == 1)
{
var res = QueryGet.First();
nOffset = res.nBlockPos + res.nBlockSize;
}
var fileReader = new BinaryReader(File.OpenRead(BlockFile));
var fileStream = fileReader.BaseStream;
var buffer = new byte[1000000]; // Max block size is 1Mb
var intBuffer = new byte[4];
fileStream.Seek(nOffset, SeekOrigin.Begin); // Seek to previous offset + previous block length
dbConn.BeginTransaction();
while (fileStream.Read(buffer, 0, 4) == 4) // Read magic number
{
var nMagic = BitConverter.ToUInt32(buffer, 0);
if (nMagic != 0xe5e9e8e4)
{
Console.WriteLine("Incorrect magic number.");
break;
}
var nBytesRead = fileStream.Read(buffer, 0, 4);
if (nBytesRead != 4)
{
Console.WriteLine("BLKSZ EOF");
break;
}
var nBlockSize = BitConverter.ToInt32(buffer, 0);
nOffset = fileStream.Position;
nBytesRead = fileStream.Read(buffer, 0, nBlockSize);
if (nBytesRead == 0 || nBytesRead != nBlockSize)
{
Console.WriteLine("BLK EOF");
break;
}
var block = new CBlock(buffer);
var headerHash = block.header.Hash;
if (nOffset % 1000 == 0) // Commit on each 1000th block
{
Console.WriteLine("Offset={0}, Hash: {1}", nOffset, headerHash);
dbConn.Commit();
dbConn.BeginTransaction();
}
if (blockMap.ContainsKey(headerHash))
{
Console.WriteLine("Duplicate block {0}", headerHash);
continue;
}
blockMap.Add(
headerHash,
new CChainNode() {
blockHeader = block.header,
blockType = block.IsProofOfStake ? BlockType.PROOF_OF_STAKE : BlockType.PROOF_OF_WORK
});
var result = dbConn.Insert(new CBlockStoreItem()
{
Hash = headerHash,
BlockHeader = block.header,
BlockTypeFlag = block.IsProofOfStake ? BlockType.PROOF_OF_STAKE : BlockType.PROOF_OF_WORK,
nBlockPos = nOffset,
nBlockSize = nBlockSize
});
}
dbConn.Commit();
fileReader.Dispose();
return true;
}
~CBlockStore()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Free other state (managed objects).
}
if (dbConn != null)
{
dbConn.Close();
dbConn = null;
}
disposed = true;
}
}
}
}