X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=Novacoin%2FCBlock.cs;h=30350b71ce333e9209f74faf66c081dde341b904;hb=4c45f0efb0bd8451c8b9a02833ce007477534751;hp=3953db12e108307999a14596abaf9435730313ce;hpb=22bf4abeb755b427bf38c88f0b9d4de5d41b27b7;p=NovacoinLibrary.git diff --git a/Novacoin/CBlock.cs b/Novacoin/CBlock.cs index 3953db1..30350b7 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 BlockException : Exception + { + public BlockException() + { + } + + public BlockException(string message) + : base(message) + { + } + + public BlockException(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); @@ -55,25 +75,33 @@ namespace Novacoin vtx[i] = new CTransaction(b.vtx[i]); } + signature = new byte[b.signature.Length]; b.signature.CopyTo(signature, 0); } /// /// 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 BlockException("Deserialization failed", e); + } } public CBlock() @@ -84,6 +112,156 @@ namespace Novacoin vtx = new CTransaction[0]; } + public bool CheckBlock(bool fCheckPOW = true, bool fCheckMerkleRoot = true, bool fCheckSig = true) + { + var uniqueTX = new List(); // tx hashes + uint nSigOps = 0; // total sigops + + // Basic sanity checkings + if (vtx.Length == 0 || Size > 1000000) + { + return false; + } + + bool fProofOfStake = IsProofOfStake; + + // First transaction must be coinbase, the rest must not be + if (!vtx[0].IsCoinBase) + { + return false; + } + + if (!vtx[0].CheckTransaction()) + { + return false; + } + + uniqueTX.Add(vtx[0].Hash); + nSigOps += vtx[0].LegacySigOpCount; + + if (fProofOfStake) + { + // Proof-of-STake related checkings. Note that we know here that 1st transactions is coinstake. We don't need + // check the type of 1st transaction because it's performed earlier by IsProofOfStake() + + // nNonce must be zero for proof-of-stake blocks + if (header.nNonce != 0) + { + return false; + } + + // Coinbase output should be empty if proof-of-stake block + if (vtx[0].vout.Length != 1 || !vtx[0].vout[0].IsEmpty) + { + return false; + } + + // Check coinstake timestamp + if (header.nTime != vtx[1].nTime) + { + return false; + } + + // Check proof-of-stake block signature + if (fCheckSig && !SignatureOK) + { + return false; + } + + if (!vtx[1].CheckTransaction()) + { + return false; + } + + uniqueTX.Add(vtx[1].Hash); + nSigOps += vtx[1].LegacySigOpCount; + } + else + { + // Check proof of work matches claimed amount + if (fCheckPOW && !CheckProofOfWork(header.Hash, header.nBits)) + { + return false; + } + + // Check timestamp + if (header.nTime > NetInfo.FutureDrift(NetInfo.GetAdjustedTime())) + { + return false; + } + + // Check coinbase timestamp + if (header.nTime < NetInfo.PastDrift(vtx[0].nTime)) + { + return false; + } + } + + // Iterate all transactions starting from second for proof-of-stake block + // or first for proof-of-work block + for (int i = fProofOfStake ? 2 : 1; i < vtx.Length; i++) + { + var tx = vtx[i]; + + // Reject coinbase transactions at non-zero index + if (tx.IsCoinBase) + { + return false; + } + + // Reject coinstake transactions at index != 1 + if (tx.IsCoinStake) + { + return false; + } + + // Check transaction timestamp + if (header.nTime < tx.nTime) + { + return false; + } + + // Check transaction consistency + if (!tx.CheckTransaction()) + { + return false; + } + + // Add transaction hash into list of unique transaction IDs + uniqueTX.Add(tx.Hash); + + // Calculate sigops count + nSigOps += tx.LegacySigOpCount; + } + + // Check for duplicate txids. + if (uniqueTX.Count != vtx.Length) + { + return false; + } + + // Reject block if validation would consume too much resources. + if (nSigOps > 50000) + { + return false; + } + + // Check merkle root + if (fCheckMerkleRoot && hashMerkleRoot != header.merkleRoot) + { + return false; + } + + return true; + } + + private bool CheckProofOfWork(ScryptHash256 hash, uint nBits) + { + // TODO: stub! + + return true; + } + /// /// Is this a Proof-of-Stake block? ///