From 5c9959adf8c0173dce35d97042f1b93d593fee0d Mon Sep 17 00:00:00 2001 From: CryptoManiac Date: Sun, 30 Aug 2015 20:35:43 +0300 Subject: [PATCH] Transaction script verification, unserealize exceptions Also, very simple and ugly blockfile reader. Only consequent reading is supported for now. --- Novacoin/CBlock.cs | 55 +++++-- Novacoin/CBlockStore.cs | 425 +++++++++++++++++++++++++++++++++++++++------- Novacoin/CTransaction.cs | 138 +++++++++++----- Novacoin/CTxIn.cs | 60 +++++-- Novacoin/Interop.cs | 2 +- NovacoinTest/Program.cs | 19 ++- 6 files changed, 561 insertions(+), 138 deletions(-) diff --git a/Novacoin/CBlock.cs b/Novacoin/CBlock.cs index 3953db1..3008db8 100644 --- a/Novacoin/CBlock.cs +++ b/Novacoin/CBlock.cs @@ -17,18 +17,34 @@ */ using System; -using System.Linq; using System.Text; using System.Collections.Generic; -using System.Security.Cryptography; using System.Diagnostics.Contracts; namespace Novacoin { - /// - /// Represents the block. Block consists of header, transaction array and header signature. - /// - public class CBlock + [Serializable] + public class BlockConstructorException : Exception + { + public BlockConstructorException() + { + } + + public BlockConstructorException(string message) + : base(message) + { + } + + public BlockConstructorException(string message, Exception inner) + : base(message, inner) + { + } + } + + /// + /// Represents the block. Block consists of header, transaction array and header signature. + /// + public class CBlock { /// /// Block header. @@ -45,6 +61,10 @@ namespace Novacoin /// public byte[] signature = new byte[0]; + /// + /// Copy constructor. + /// + /// CBlock instance. public CBlock(CBlock b) { header = new CBlockHeader(b.header); @@ -61,19 +81,26 @@ namespace Novacoin /// /// Parse byte sequence and initialize new block instance /// - /// + /// Bytes sequence. public CBlock (byte[] blockBytes) { - ByteQueue wBytes = new ByteQueue(blockBytes); + try + { + ByteQueue wBytes = new ByteQueue(blockBytes); - // Fill the block header fields - header = new CBlockHeader(wBytes.Get(80)); + // Fill the block header fields + header = new CBlockHeader(wBytes.Get(80)); - // Parse transactions list - vtx = CTransaction.ReadTransactionsList(ref wBytes); + // Parse transactions list + vtx = CTransaction.ReadTransactionsList(ref wBytes); - // Read block signature - signature = wBytes.Get((int)wBytes.GetVarInt()); + // Read block signature + signature = wBytes.Get((int)wBytes.GetVarInt()); + } + catch (Exception e) + { + throw new BlockConstructorException("Deserealization failed", e); + } } public CBlock() diff --git a/Novacoin/CBlockStore.cs b/Novacoin/CBlockStore.cs index 742a849..540012b 100644 --- a/Novacoin/CBlockStore.cs +++ b/Novacoin/CBlockStore.cs @@ -1,23 +1,20 @@ using System; using System.IO; using System.Linq; -using System.Collections.Generic; +using System.Collections.Concurrent; using SQLite.Net; using SQLite.Net.Attributes; using SQLite.Net.Interop; using SQLite.Net.Platform.Generic; +using SQLiteNetExtensions.Attributes; + namespace Novacoin { - public enum BlockType - { - PROOF_OF_WORK, - PROOF_OF_WORK_MODIFIER, - PROOF_OF_STAKE, - PROOF_OF_STAKE_MODIFIER - }; - + /// + /// Block headers table + /// [Table("BlockStorage")] class CBlockStoreItem { @@ -34,14 +31,34 @@ namespace Novacoin public byte[] Hash { get; set; } /// - /// Next block hash + /// Version of block schema /// - public byte[] NextHash { get; set; } + public uint nVersion { get; set; } + + /// + /// Previous block hash. + /// + public byte[] prevHash { get; set; } + + /// + /// Merkle root hash. + /// + public byte[] merkleRoot { get; set; } + + /// + /// Block timestamp. + /// + public uint nTime { get; set; } /// - /// Serialized representation of block header + /// Compressed difficulty representation. /// - public byte[] BlockHeader { get; set; } + public uint nBits { get; set; } + + /// + /// Nonce counter. + /// + public uint nNonce { get; set; } /// /// Block type flags @@ -49,6 +66,11 @@ namespace Novacoin public BlockType BlockTypeFlag { get; set; } /// + /// Next block hash + /// + public byte[] NextHash { get; set; } + + /// /// Block position in file /// public long nBlockPos { get; set; } @@ -57,6 +79,170 @@ namespace Novacoin /// Block size in bytes /// public int nBlockSize { get; set; } + + /// + /// Fill database item with data from given block header. + /// + /// Block header + /// Header hash + public ScryptHash256 FillHeader(CBlockHeader header) + { + ScryptHash256 _hash; + Hash = _hash = header.Hash; + + nVersion = header.nVersion; + prevHash = header.prevHash; + merkleRoot = header.merkleRoot; + nTime = header.nTime; + nBits = header.nBits; + nNonce = header.nNonce; + + return _hash; + } + + /// + /// Reconstruct block header from item data. + /// + public CBlockHeader BlockHeader + { + get + { + CBlockHeader header = new CBlockHeader(); + + header.nVersion = nVersion; + header.prevHash = new Hash256(prevHash); + header.merkleRoot = new Hash256(merkleRoot); + header.nTime = nTime; + header.nBits = nBits; + header.nNonce = nNonce; + + return header; + } + } + + /// + /// Read block from file. + /// + /// Stream with read access. + /// CBlock reference. + /// Result + public bool ReadFromFile(ref Stream reader, out CBlock block) + { + var buffer = new byte[nBlockSize]; + block = null; + + try + { + reader.Seek(nBlockPos, SeekOrigin.Begin); + + if (nBlockSize != reader.Read(buffer, 0, nBlockSize)) + { + return false; + } + + block = new CBlock(buffer); + + return true; + } + catch (IOException) + { + // I/O error + return false; + } + catch (BlockConstructorException) + { + // Constructor exception + return false; + } + } + } + + /// + /// Block type. + /// + public enum BlockType + { + PROOF_OF_WORK, + PROOF_OF_WORK_MODIFIER, + PROOF_OF_STAKE, + PROOF_OF_STAKE_MODIFIER + }; + + /// + /// Transaction type. + /// + public enum TxType + { + TX_COINBASE, + TX_COINSTAKE, + TX_USER + } + + [Table("TransactionStorage")] + public class CTransactionStoreItem + { + /// + /// Transaction hash + /// + [PrimaryKey] + public byte[] TransactionHash { get; set; } + + /// + /// Block hash + /// + [ForeignKey(typeof(CBlockStoreItem), Name = "Hash")] + public byte[] BlockHash { get; set; } + + /// + /// Transaction type flag + /// + public TxType txType { get; set; } + + /// + /// Tx position in file + /// + public long nTxPos { get; set; } + + /// + /// Transaction size + /// + public int nTxSize { get; set; } + + /// + /// Read transaction from file. + /// + /// Stream with read access. + /// CTransaction reference. + /// Result + public bool ReadFromFile(ref Stream reader, out CTransaction tx) + { + var buffer = new byte[250000]; // Max transaction size is 250kB + tx = null; + + try + { + reader.Seek(nTxPos, SeekOrigin.Begin); // Seek to transaction offset + + if (nTxSize != reader.Read(buffer, 0, nTxSize)) + { + return false; + } + + tx = new CTransaction(buffer); + + return true; + } + catch (IOException) + { + // I/O error + return false; + } + catch (TransactionConstructorException) + { + // Constructor error + return false; + } + } } /// @@ -90,16 +276,28 @@ namespace Novacoin private bool disposed = false; private object LockObj = new object(); private SQLiteConnection dbConn = null; + private string strBlockFile; + + private ConcurrentDictionary blockMap = new ConcurrentDictionary(); + private ConcurrentDictionary txMap = new ConcurrentDictionary(); + private CBlock genesisBlock = new CBlock(Interop.HexToArray("0100000000000000000000000000000000000000000000000000000000000000000000007b0502ad2f9f675528183f83d6385794fbcaa914e6d385c6cb1d866a3b3bb34c398e1151ffff0f1ed30918000101000000398e1151010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d020f274468747470733a2f2f626974636f696e74616c6b2e6f72672f696e6465782e7068703f746f7069633d3133343137392e6d736731353032313936236d736731353032313936ffffffff010000000000000000000000000000")); + + public static CBlockStore Instance; + + /// + /// Block file stream + /// + private Stream reader; - 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") + public CBlockStore(string IndexDB = "blockstore.dat", string BlockFile = "bootstrap.dat") { + strBlockFile = BlockFile; + bool firstInit = !File.Exists(IndexDB); dbConn = new SQLiteConnection(new SQLitePlatformGeneric(), IndexDB); @@ -107,28 +305,88 @@ namespace Novacoin { lock (LockObj) { + // Create tables dbConn.CreateTable(CreateFlags.AutoIncPK); + dbConn.CreateTable(CreateFlags.ImplicitPK); + + // Init store with genesis block + + var NewBlockItem = new CBlockStoreItem() + { + BlockTypeFlag = genesisBlock.IsProofOfStake ? BlockType.PROOF_OF_STAKE : BlockType.PROOF_OF_WORK, + nBlockPos = 8, + nBlockSize = ((byte[])genesisBlock).Length + }; + + var HeaderHash = NewBlockItem.FillHeader(genesisBlock.header); + var NewNode = new CChainNode() { blockHeader = genesisBlock.header, blockType = BlockType.PROOF_OF_WORK }; + + blockMap.TryAdd(HeaderHash, NewNode); + dbConn.Insert(NewBlockItem); + + var NewTxItem = new CTransactionStoreItem() + { + TransactionHash = genesisBlock.vtx[0].Hash, + BlockHash = HeaderHash, + txType = TxType.TX_COINBASE, + nTxPos = 8 + genesisBlock.GetTxOffset(0), + nTxSize = genesisBlock.vtx[0].Size + }; + + dbConn.Insert(NewTxItem); } } else { var QueryGet = dbConn.Query("select * from [BlockStorage] order by [ItemId] asc"); - foreach (CBlockStoreItem si in QueryGet) + // Init list of block items + foreach (var storeItem in QueryGet) { - blockMap.Add( - new ScryptHash256(si.Hash), - new CChainNode() - { - blockHeader = new CBlockHeader(si.BlockHeader), - blockType = si.BlockTypeFlag - }); + var currentNode = new CChainNode() { blockHeader = new CBlockHeader(storeItem.BlockHeader), blockType = storeItem.BlockTypeFlag }; + blockMap.TryAdd(new ScryptHash256(storeItem.Hash), currentNode); } } + + var fStream1 = File.OpenRead(strBlockFile); + reader = new BinaryReader(fStream1).BaseStream; + + Instance = this; + + } + + public bool GetTransaction(Hash256 TxID, ref CTransaction tx) + { + var QueryTx = dbConn.Query("select * from [TransactionStorage] where [TransactionHash] = ?", (byte[])TxID); + + if (QueryTx.Count == 1) + { + return QueryTx[0].ReadFromFile(ref reader, out tx); + } + + // Tx not found + + return false; + } + + public bool GetBlock(ScryptHash256 blockHash, ref CBlock block) + { + var QueryBlock = dbConn.Query("select * from [BlockStorage] where [Hash] = ?", (byte[])blockHash); + + if (QueryBlock.Count == 1) + { + return QueryBlock[0].ReadFromFile(ref reader, out block); + } + + // Block not found + + return false; } public bool ParseBlockFile(string BlockFile = "bootstrap.dat") { + strBlockFile = BlockFile; + // TODO: Rewrite completely. var QueryGet = dbConn.Query("select * from [BlockStorage] order by [ItemId] desc limit 1"); @@ -141,80 +399,129 @@ namespace Novacoin 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 + var fStream2 = File.OpenRead(strBlockFile); + var readerForBlocks = new BinaryReader(fStream2).BaseStream; + + readerForBlocks.Seek(nOffset, SeekOrigin.Begin); // Seek to previous offset + previous block length dbConn.BeginTransaction(); - while (fileStream.Read(buffer, 0, 4) == 4) // Read magic number + while (readerForBlocks.Read(buffer, 0, 4) == 4) // Read magic number { var nMagic = BitConverter.ToUInt32(buffer, 0); if (nMagic != 0xe5e9e8e4) { - Console.WriteLine("Incorrect magic number."); - break; + throw new Exception("Incorrect magic number."); } - var nBytesRead = fileStream.Read(buffer, 0, 4); + var nBytesRead = readerForBlocks.Read(buffer, 0, 4); if (nBytesRead != 4) { - Console.WriteLine("BLKSZ EOF"); - break; + throw new Exception("BLKSZ EOF"); } var nBlockSize = BitConverter.ToInt32(buffer, 0); - nOffset = fileStream.Position; + nOffset = readerForBlocks.Position; - nBytesRead = fileStream.Read(buffer, 0, nBlockSize); + nBytesRead = readerForBlocks.Read(buffer, 0, nBlockSize); if (nBytesRead == 0 || nBytesRead != nBlockSize) { - Console.WriteLine("BLK EOF"); - break; + throw new Exception("BLK EOF"); } var block = new CBlock(buffer); - var headerHash = block.header.Hash; - if (nOffset % 1000 == 0) // Commit on each 1000th block + if (block.header.merkleRoot != block.hashMerkleRoot) { - Console.WriteLine("Offset={0}, Hash: {1}", nOffset, headerHash); - dbConn.Commit(); - dbConn.BeginTransaction(); + Console.WriteLine("MerkleRoot mismatch: {0} vs. {1} in block {2}.", block.header.merkleRoot, block.hashMerkleRoot, block.header.Hash); + continue; } - if (blockMap.ContainsKey(headerHash)) + if (block.IsProofOfStake && !block.SignatureOK) { - Console.WriteLine("Duplicate block {0}", headerHash); + Console.WriteLine("Proof-of-Stake signature is invalid for block {0}.", block.header.Hash); 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() + var NewStoreItem = new CBlockStoreItem() { - Hash = headerHash, - BlockHeader = block.header, BlockTypeFlag = block.IsProofOfStake ? BlockType.PROOF_OF_STAKE : BlockType.PROOF_OF_WORK, nBlockPos = nOffset, nBlockSize = nBlockSize - }); + }; + + var NewChainNode = new CChainNode() + { + blockHeader = block.header, + blockType = NewStoreItem.BlockTypeFlag + }; + + var HeaderHash = NewStoreItem.FillHeader(block.header); + + int nCount = blockMap.Count; + Console.WriteLine("nCount={0}, Hash={1}, Time={2}", nCount, HeaderHash, DateTime.Now); // Commit on each 100th block + + if (nCount % 100 == 0) + { + Console.WriteLine("Commit..."); + dbConn.Commit(); + dbConn.BeginTransaction(); + } + + if (!blockMap.TryAdd(HeaderHash, NewChainNode)) + { + Console.WriteLine("Duplicate block: {0}", HeaderHash); + continue; + } + + // Verify transactions + + foreach (var tx in block.vtx) + { + if (!tx.VerifyScripts()) + { + Console.WriteLine("Error checking tx {0}", tx.Hash); + continue; + } + } + + dbConn.Insert(NewStoreItem); + + for (int i = 0; i < block.vtx.Length; i++) + { + // Handle trasactions + + var nTxOffset = nOffset + block.GetTxOffset(i); + TxType txnType = TxType.TX_USER; + + if (block.vtx[i].IsCoinBase) + { + txnType = TxType.TX_COINBASE; + } + else if (block.vtx[i].IsCoinStake) + { + txnType = TxType.TX_COINSTAKE; + } + + var NewTxItem = new CTransactionStoreItem() + { + TransactionHash = block.vtx[i].Hash, + BlockHash = HeaderHash, + nTxPos = nTxOffset, + nTxSize = block.vtx[i].Size, + txType = txnType + }; + + dbConn.Insert(NewTxItem); + } } dbConn.Commit(); - - fileReader.Dispose(); return true; } @@ -237,6 +544,8 @@ namespace Novacoin if (disposing) { // Free other state (managed objects). + + reader.Dispose(); } if (dbConn != null) diff --git a/Novacoin/CTransaction.cs b/Novacoin/CTransaction.cs index fd8c28f..e9a1445 100644 --- a/Novacoin/CTransaction.cs +++ b/Novacoin/CTransaction.cs @@ -22,6 +22,24 @@ using System.Collections.Generic; namespace Novacoin { + [Serializable] + public class TransactionConstructorException : Exception + { + public TransactionConstructorException() + { + } + + public TransactionConstructorException(string message) + : base(message) + { + } + + public TransactionConstructorException(string message, Exception inner) + : base(message, inner) + { + } + } + /// /// Represents the transaction. Any transaction must provide one input and one output at least. /// @@ -93,6 +111,27 @@ namespace Novacoin nLockTime = tx.nLockTime; } + public bool VerifyScripts() + { + if (IsCoinBase) + { + return true; + } + + CTransaction txPrev = null; + for (int i = 0; i < vin.Length; i++) + { + var outpoint = vin[i].prevout; + + if (!CBlockStore.Instance.GetTransaction(outpoint.hash, ref txPrev)) + return false; + + if (!ScriptCode.VerifyScript(vin[i].scriptSig, txPrev.vout[outpoint.n].scriptPubKey, this, i, (int)scriptflag.SCRIPT_VERIFY_P2SH, 0)) + return false; + } + + return true; + } /// /// Parse byte sequence and initialize new instance of CTransaction @@ -100,41 +139,48 @@ namespace Novacoin /// Byte sequence public CTransaction(byte[] txBytes) { - var wBytes = new ByteQueue(txBytes); + try + { + var wBytes = new ByteQueue(txBytes); - nVersion = BitConverter.ToUInt32(wBytes.Get(4), 0); - nTime = BitConverter.ToUInt32(wBytes.Get(4), 0); + nVersion = BitConverter.ToUInt32(wBytes.Get(4), 0); + nTime = BitConverter.ToUInt32(wBytes.Get(4), 0); - int nInputs = (int)wBytes.GetVarInt(); - vin = new CTxIn[nInputs]; + int nInputs = (int)wBytes.GetVarInt(); + vin = new CTxIn[nInputs]; - for (int nCurrentInput = 0; nCurrentInput < nInputs; nCurrentInput++) - { - // Fill inputs array - vin[nCurrentInput] = new CTxIn(); - - vin[nCurrentInput].prevout = new COutPoint(wBytes.Get(36)); + for (int nCurrentInput = 0; nCurrentInput < nInputs; nCurrentInput++) + { + // Fill inputs array + vin[nCurrentInput] = new CTxIn(); - int nScriptSigLen = (int)wBytes.GetVarInt(); - vin[nCurrentInput].scriptSig = new CScript(wBytes.Get(nScriptSigLen)); + vin[nCurrentInput].prevout = new COutPoint(wBytes.Get(36)); - vin[nCurrentInput].nSequence = BitConverter.ToUInt32(wBytes.Get(4), 0); - } + int nScriptSigLen = (int)wBytes.GetVarInt(); + vin[nCurrentInput].scriptSig = new CScript(wBytes.Get(nScriptSigLen)); - int nOutputs = (int)wBytes.GetVarInt(); - vout = new CTxOut[nOutputs]; + vin[nCurrentInput].nSequence = BitConverter.ToUInt32(wBytes.Get(4), 0); + } - for (int nCurrentOutput = 0; nCurrentOutput < nOutputs; nCurrentOutput++) - { - // Fill outputs array - vout[nCurrentOutput] = new CTxOut(); - vout[nCurrentOutput].nValue = BitConverter.ToInt64(wBytes.Get(8), 0); + int nOutputs = (int)wBytes.GetVarInt(); + vout = new CTxOut[nOutputs]; - int nScriptPKLen = (int)wBytes.GetVarInt(); - vout[nCurrentOutput].scriptPubKey = new CScript(wBytes.Get(nScriptPKLen)); - } + for (int nCurrentOutput = 0; nCurrentOutput < nOutputs; nCurrentOutput++) + { + // Fill outputs array + vout[nCurrentOutput] = new CTxOut(); + vout[nCurrentOutput].nValue = BitConverter.ToUInt64(wBytes.Get(8), 0); + + int nScriptPKLen = (int)wBytes.GetVarInt(); + vout[nCurrentOutput].scriptPubKey = new CScript(wBytes.Get(nScriptPKLen)); + } - nLockTime = BitConverter.ToUInt32(wBytes.Get(4), 0); + nLockTime = BitConverter.ToUInt32(wBytes.Get(4), 0); + } + catch (Exception e) + { + throw new TransactionConstructorException("Deserialization failed", e); + } } /// @@ -170,28 +216,36 @@ namespace Novacoin /// Transactions array public static CTransaction[] ReadTransactionsList(ref ByteQueue wTxBytes) { - // Read amount of transactions - int nTransactions = (int)wTxBytes.GetVarInt(); - var tx = new CTransaction[nTransactions]; - - for (int nTx = 0; nTx < nTransactions; nTx++) + try { - // Fill the transactions array - tx[nTx] = new CTransaction(); + // Read amount of transactions + int nTransactions = (int)wTxBytes.GetVarInt(); + var tx = new CTransaction[nTransactions]; - tx[nTx].nVersion = BitConverter.ToUInt32(wTxBytes.Get(4), 0); - tx[nTx].nTime = BitConverter.ToUInt32(wTxBytes.Get(4), 0); + for (int nTx = 0; nTx < nTransactions; nTx++) + { + // Fill the transactions array + tx[nTx] = new CTransaction(); - // Inputs array - tx[nTx].vin = CTxIn.ReadTxInList(ref wTxBytes); + tx[nTx].nVersion = BitConverter.ToUInt32(wTxBytes.Get(4), 0); + tx[nTx].nTime = BitConverter.ToUInt32(wTxBytes.Get(4), 0); - // outputs array - tx[nTx].vout = CTxOut.ReadTxOutList(ref wTxBytes); + // Inputs array + tx[nTx].vin = CTxIn.ReadTxInList(ref wTxBytes); - tx[nTx].nLockTime = BitConverter.ToUInt32(wTxBytes.Get(4), 0); - } + // outputs array + tx[nTx].vout = CTxOut.ReadTxOutList(ref wTxBytes); + + tx[nTx].nLockTime = BitConverter.ToUInt32(wTxBytes.Get(4), 0); + } + + return tx; - return tx; + } + catch (Exception e) + { + throw new TransactionConstructorException("Deserealization failed", e); + } } public bool IsCoinBase diff --git a/Novacoin/CTxIn.cs b/Novacoin/CTxIn.cs index 937b367..cd205ab 100644 --- a/Novacoin/CTxIn.cs +++ b/Novacoin/CTxIn.cs @@ -22,10 +22,28 @@ using System.Collections.Generic; namespace Novacoin { - /// - /// Transaction input. - /// - public class CTxIn + [Serializable] + public class TxInConstructorException : Exception + { + public TxInConstructorException() + { + } + + public TxInConstructorException(string message) + : base(message) + { + } + + public TxInConstructorException(string message, Exception inner) + : base(message, inner) + { + } + } + + /// + /// Transaction input. + /// + public class CTxIn { /// /// Previous input data @@ -69,21 +87,28 @@ namespace Novacoin /// Inputs array public static CTxIn[] ReadTxInList(ref ByteQueue wBytes) { - // Get amount - int nInputs = (int)wBytes.GetVarInt(); - var vin = new CTxIn[nInputs]; - - for (int nIndex = 0; nIndex < nInputs; nIndex++) + try { - // Fill inputs array - vin[nIndex] = new CTxIn(); - vin[nIndex].prevout = new COutPoint(wBytes.Get(36)); - vin[nIndex].scriptSig = new CScript(wBytes.Get((int)wBytes.GetVarInt())); - vin[nIndex].nSequence = BitConverter.ToUInt32(wBytes.Get(4), 0); + // Get amount + int nInputs = (int)wBytes.GetVarInt(); + var vin = new CTxIn[nInputs]; + + for (int nIndex = 0; nIndex < nInputs; nIndex++) + { + // Fill inputs array + vin[nIndex] = new CTxIn(); + vin[nIndex].prevout = new COutPoint(wBytes.Get(36)); + vin[nIndex].scriptSig = new CScript(wBytes.Get((int)wBytes.GetVarInt())); + vin[nIndex].nSequence = BitConverter.ToUInt32(wBytes.Get(4), 0); + } + + // Return inputs array + return vin; + } + catch (Exception e) + { + throw new TxInConstructorException("Deserealization failed.", e); } - - // Return inputs array - return vin; } /// @@ -152,4 +177,3 @@ namespace Novacoin } } - diff --git a/Novacoin/Interop.cs b/Novacoin/Interop.cs index 6771afd..5c6d317 100644 --- a/Novacoin/Interop.cs +++ b/Novacoin/Interop.cs @@ -56,7 +56,7 @@ namespace Novacoin return bytes; } - public static string ToHex(IEnumerable bytes) + public static string ToHex(byte[] bytes) { var sb = new StringBuilder(); foreach (var b in bytes) diff --git a/NovacoinTest/Program.cs b/NovacoinTest/Program.cs index 933481a..3ccddf4 100644 --- a/NovacoinTest/Program.cs +++ b/NovacoinTest/Program.cs @@ -79,7 +79,7 @@ namespace NovacoinTest /// ECDSA keypair signing test var data = "Превед!"; - var sigHash = Hash256.Compute256(Encoding.UTF8.GetBytes(data)); + var sigHash = Hash256.Compute256(Encoding.UTF8.GetBytes(data)); var signature = keyPair1.Sign(sigHash); Console.WriteLine("Signature: {0}", Interop.ToHex(signature)); @@ -122,7 +122,7 @@ namespace NovacoinTest /// Some SetDestination tests var scriptDestinationTest = new CScript(); - + Console.WriteLine("Creating and decoding new destination with {0} as public key.\n", keyPair1.PubKey.ToString()); Console.WriteLine("Pay-to-Pubkey:"); @@ -276,15 +276,24 @@ namespace NovacoinTest ScryptHash256 hash2 = b2.header.Hash; ScryptHash256 hash3 = veryBigBlock.header.Hash; - Console.WriteLine("{0} < {1} : {2}", hash1.ToString(), hash3.ToString(), hash1 < hash3); + Console.WriteLine("\n{0} < {1} : {2}", hash1.ToString(), hash3.ToString(), hash1 < hash3); Console.WriteLine("{0} > {1} : {2}", hash1.ToString(), hash3.ToString(), hash1 > hash3); Console.WriteLine("{0} <= {1} : {2}", hash1.ToString(), hash2.ToString(), hash1 <= hash2); Console.WriteLine("{0} >= {1} : {2}", hash1.ToString(), hash2.ToString(), hash1 >= hash2); Console.WriteLine("{0} != {1} : {2}", hash1.ToString(), hash2.ToString(), hash1 != hash2); - Console.WriteLine("{0} == {1} : {2}", hash2.ToString(), hash3.ToString(), hash2 == hash3); + Console.WriteLine("{0} == {1} : {2}\n", hash2.ToString(), hash3.ToString(), hash2 == hash3); + + /* + /// Pre-09854c5 revisions were affected by integer overflow bug, this issue was caused by incorrect deserialization of input value. Below you can see an example, broken transaction and its normal version. + + var txBrokenByBug = new CTransaction(Interop.HexToArray("010000007a0f195109057f38eecc438221e7c53158e9ee7ce501818d2b4c14e41269b86e799624c6af010000006c493046022100dcaee50bc231857860f5e0a50f02bb43fd688a69c6b2b91e30f4ea44cb931bf30221008adb50edce6277ee030e3ac0c9f2f62b8b074d68c44a9a306d58eebf08473f65012103c84e4d660e1e637fd17d6b764d4300c4dd04b8dc9ddac31ac820b5eface4e152ffffffffb1cec18b7d68b4c527cefb8e0495ec68de67f960a83b650e54cd7deed76c5667000000004847304402206e0641d7973539a0da5e014232726e900aafcb86737375632226bdf94fcce836022014b855f588b1694806148157245f56dc3396f7dc6816fb4838ddd48bd73de3d901ffffffffc682526404ea8bd6f7dcf0a0003d755621b5868ee8d36c6bb4b9c859f537d053000000004847304402206dabd11d6c65cb1d53b6574f1ec171a7f81f201975cd8ebedb84abf352fb73a0022056880ca946eb3900561fc3f07b3e4e9647d83b032ea0291469d63f54b8020a0a01ffffffffd9c953ad35321a9fb600482e10baafd5f549deccc3414f70e04368ce1ee6a8a3010000006c493046022100c6431d3d7803772e5930cbde7bf7f5d20aba20534cbe3fb694cd5d3dbd8fdef002210082f7c787a80f94956fec2cd55ade6a32328f044dba3b5d3cdfb165e6b25c69d5012103c84e4d660e1e637fd17d6b764d4300c4dd04b8dc9ddac31ac820b5eface4e152ffffffffdea32fcf61c6d6ed95803e63b22f1395d5306ae87eb42beeb78f2b5389e19af2010000006b4830450221009e2f4c9b87094a1a8ce1d00f1a45dadb2a4fe3fa61da08c1f98e65b76770e05e02201f3a90c8c998b02efe58f583c1902646844ac658ce2b5767e00f84f54df1a9c4012103c84e4d660e1e637fd17d6b764d4300c4dd04b8dc9ddac31ac820b5eface4e152ffffffffeef4abc62727f3e52fadb952fa943c478b740a3d4f66a6626e380804875dab5c000000004a493046022100b70793134908ebf74dcab33a5f60fa91453cf3266c1714921f7f656dee36adaf022100d121892a94bf3caac713bc2e157cb8f5e032b0d77fa6173bb9626ebd645fd35501fffffffffbb6d8c93d4bbc2f05442778c31a4fa651659acdeaf980fef0a4aa4b163a590e010000006c493046022100ce375b454f80f35afad4d07187bd2a60fb4016b070d1d5c64ffbc1a7a4aef0d7022100ae876f1d30233900db5c532b1eecb4effade843de7414c1f5c11a3a94a810e35012103c84e4d660e1e637fd17d6b764d4300c4dd04b8dc9ddac31ac820b5eface4e152fffffffffe4ac77a8918638c1e470b049ef8b75fc3a109e56a1bcbb7fddf39b4d0fa1f4b010000006c493046022100a2b33c2e1ce08e1883f35bde4c444bf82eb07210e764146a538e3026d68cc177022100e8e0fa3971078d5d917701c53b4bca70391ff48ea2fc64ad53ed90a345cbdccc012103c84e4d660e1e637fd17d6b764d4300c4dd04b8dc9ddac31ac820b5eface4e152ffffffff3835596d05c57f778729b391f98ff34358b72e5cc0812849f3548ac995dfde18000000004847304402205d17a90cbb1b2ed79e8ff7ecf6e851f750991d7b544ac897ee5195b34c51fe8d02207eb6f420423f680a9e0e4cb463b8bffe255648bbc96a707dfaf045f28dc6fa1601ffffffff0210270000000000001976a91466447313fd3230e5f8adfd5425bef17c96d9917288ac00f2052a000000001976a914e04cad5597da08b244b59be6b881f8934537eb4d88ac00000000")); + var txNoBug = new CTransaction(Interop.HexToArray("010000007a0f195109057f38eecc438221e7c53158e9ee7ce501818d2b4c14e41269b86e799624c6af010000006c493046022100dcaee50bc231857860f5e0a50f02bb43fd688a69c6b2b91e30f4ea44cb931bf30221008adb50edce6277ee030e3ac0c9f2f62b8b074d68c44a9a306d58eebf08473f65012103c84e4d660e1e637fd17d6b764d4300c4dd04b8dc9ddac31ac820b5eface4e152ffffffffb1cec18b7d68b4c527cefb8e0495ec68de67f960a83b650e54cd7deed76c5667000000004847304402206e0641d7973539a0da5e014232726e900aafcb86737375632226bdf94fcce836022014b855f588b1694806148157245f56dc3396f7dc6816fb4838ddd48bd73de3d901ffffffffc682526404ea8bd6f7dcf0a0003d755621b5868ee8d36c6bb4b9c859f537d053000000004847304402206dabd11d6c65cb1d53b6574f1ec171a7f81f201975cd8ebedb84abf352fb73a0022056880ca946eb3900561fc3f07b3e4e9647d83b032ea0291469d63f54b8020a0a01ffffffffd9c953ad35321a9fb600482e10baafd5f549deccc3414f70e04368ce1ee6a8a3010000006c493046022100c6431d3d7803772e5930cbde7bf7f5d20aba20534cbe3fb694cd5d3dbd8fdef002210082f7c787a80f94956fec2cd55ade6a32328f044dba3b5d3cdfb165e6b25c69d5012103c84e4d660e1e637fd17d6b764d4300c4dd04b8dc9ddac31ac820b5eface4e152ffffffffdea32fcf61c6d6ed95803e63b22f1395d5306ae87eb42beeb78f2b5389e19af2010000006b4830450221009e2f4c9b87094a1a8ce1d00f1a45dadb2a4fe3fa61da08c1f98e65b76770e05e02201f3a90c8c998b02efe58f583c1902646844ac658ce2b5767e00f84f54df1a9c4012103c84e4d660e1e637fd17d6b764d4300c4dd04b8dc9ddac31ac820b5eface4e152ffffffffeef4abc62727f3e52fadb952fa943c478b740a3d4f66a6626e380804875dab5c000000004a493046022100b70793134908ebf74dcab33a5f60fa91453cf3266c1714921f7f656dee36adaf022100d121892a94bf3caac713bc2e157cb8f5e032b0d77fa6173bb9626ebd645fd35501fffffffffbb6d8c93d4bbc2f05442778c31a4fa651659acdeaf980fef0a4aa4b163a590e010000006c493046022100ce375b454f80f35afad4d07187bd2a60fb4016b070d1d5c64ffbc1a7a4aef0d7022100ae876f1d30233900db5c532b1eecb4effade843de7414c1f5c11a3a94a810e35012103c84e4d660e1e637fd17d6b764d4300c4dd04b8dc9ddac31ac820b5eface4e152fffffffffe4ac77a8918638c1e470b049ef8b75fc3a109e56a1bcbb7fddf39b4d0fa1f4b010000006c493046022100a2b33c2e1ce08e1883f35bde4c444bf82eb07210e764146a538e3026d68cc177022100e8e0fa3971078d5d917701c53b4bca70391ff48ea2fc64ad53ed90a345cbdccc012103c84e4d660e1e637fd17d6b764d4300c4dd04b8dc9ddac31ac820b5eface4e152ffffffff3835596d05c57f778729b391f98ff34358b72e5cc0812849f3548ac995dfde18000000004847304402205d17a90cbb1b2ed79e8ff7ecf6e851f750991d7b544ac897ee5195b34c51fe8d02207eb6f420423f680a9e0e4cb463b8bffe255648bbc96a707dfaf045f28dc6fa1601ffffffff0210270000000000001976a91466447313fd3230e5f8adfd5425bef17c96d9917288ac00f2052a010000001976a914e04cad5597da08b244b59be6b881f8934537eb4d88ac00000000")); + Console.WriteLine(txBrokenByBug); + Console.WriteLine(txNoBug); + */ - /* + /* Console.WriteLine("Reading the block file..."); var bs = new CBlockStore(); bs.ParseBlockFile(); -- 1.7.1