X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=Novacoin%2FCBlockStore.cs;h=cca056e90a9855309496a5220f247ab038e5fbbb;hb=7d59deb2e70c69cad8526523f90b3fbdbfe40e17;hp=f44a93d5613c7ab7ca773e2052ef7d2ba22d2daf;hpb=7b349e848699cdb1701a69350b4f677a43831931;p=NovacoinLibrary.git diff --git a/Novacoin/CBlockStore.cs b/Novacoin/CBlockStore.cs index f44a93d..cca056e 100644 --- a/Novacoin/CBlockStore.cs +++ b/Novacoin/CBlockStore.cs @@ -85,7 +85,7 @@ namespace Novacoin private ConcurrentDictionary mapStakeSeen = new ConcurrentDictionary(); - private ConcurrentDictionary mapStakeSeenOrphan = new ConcurrentDictionary(); + private ConcurrentDictionary, uint256> mapStakeSeenOrphan = new ConcurrentDictionary, uint256>(); /// @@ -214,7 +214,7 @@ namespace Novacoin } } - public bool GetTxOutCursor(COutPoint outpoint, ref TxOutItem txOutCursor) + public bool GetTxOutCursor(COutPoint outpoint, out TxOutItem txOutCursor) { var queryResults = dbConn.Query("select o.* from [Outputs] o left join [MerkleNodes] m on (m.nMerkleNodeID = o.nMerkleNodeID) where m.[TransactionHash] = ?", (byte[])outpoint.hash); @@ -227,10 +227,12 @@ namespace Novacoin // Tx not found + txOutCursor = null; + return false; } - public bool FetchInputs(CTransaction tx, ref Dictionary queued, ref Dictionary inputs, bool IsBlock, out bool Invalid) + public bool FetchInputs(ref CTransaction tx, ref Dictionary queued, ref Dictionary inputs, bool IsBlock, out bool Invalid) { Invalid = false; @@ -348,17 +350,6 @@ namespace Novacoin return false; // SetStakeEntropyBit() failed } - // Save proof-of-stake hash value - if (itemTemplate.IsProofOfStake) - { - uint256 hashProofOfStake; - if (!GetProofOfStakeHash(blockHash, out hashProofOfStake)) - { - return false; // hashProofOfStake not found - } - itemTemplate.hashProofOfStake = hashProofOfStake; - } - // compute stake modifier long nStakeModifier = 0; bool fGeneratedStakeModifier = false; @@ -368,7 +359,7 @@ namespace Novacoin } itemTemplate.SetStakeModifier(nStakeModifier, fGeneratedStakeModifier); - itemTemplate.nStakeModifierChecksum = StakeModifier.GetStakeModifierChecksum(itemTemplate); + itemTemplate.nStakeModifierChecksum = StakeModifier.GetStakeModifierChecksum(ref itemTemplate); // TODO: verify stake modifier checkpoints @@ -379,6 +370,14 @@ namespace Novacoin itemTemplate.prevoutStake = block.vtx[1].vin[0].prevout; itemTemplate.nStakeTime = block.vtx[1].nTime; + + // Save proof-of-stake hash value + uint256 hashProofOfStake; + if (!GetProofOfStakeHash(ref blockHash, out hashProofOfStake)) + { + return false; // hashProofOfStake not found + } + itemTemplate.hashProofOfStake = hashProofOfStake; } if (!itemTemplate.WriteToFile(ref fStreamReadWrite, ref block)) @@ -554,7 +553,6 @@ namespace Novacoin } } - // Connect longer branch var txDelete = new List(); foreach (var cursor in connect) @@ -650,9 +648,9 @@ namespace Novacoin bool fScriptChecks = cursor.nHeight >= Checkpoints.TotalBlocksEstimate; var scriptFlags = scriptflag.SCRIPT_VERIFY_NOCACHE | scriptflag.SCRIPT_VERIFY_P2SH; - ulong nFees = 0; - ulong nValueIn = 0; - ulong nValueOut = 0; + long nFees = 0; + long nValueIn = 0; + long nValueOut = 0; uint nSigOps = 0; var queuedMerkleNodes = new Dictionary(); @@ -694,7 +692,7 @@ namespace Novacoin else { bool Invalid; - if (!FetchInputs(tx, ref queuedOutputs, ref inputs, true, out Invalid)) + if (!FetchInputs(ref tx, ref queuedOutputs, ref inputs, true, out Invalid)) { return false; // Unable to fetch some inputs. } @@ -708,8 +706,8 @@ namespace Novacoin return false; // too many sigops } - ulong nTxValueIn = tx.GetValueIn(ref inputs); - ulong nTxValueOut = tx.nValueOut; + long nTxValueIn = tx.GetValueIn(ref inputs); + long nTxValueOut = tx.nValueOut; nValueIn += nTxValueIn; nValueOut += nTxValueOut; @@ -719,7 +717,7 @@ namespace Novacoin nFees += nTxValueIn - nTxValueOut; } - if (!ConnectInputs(tx, ref inputs, ref queuedOutputs, ref cursor, true, fScriptChecks, scriptFlags)) + if (!ConnectInputs(ref tx, ref inputs, ref queuedOutputs, ref cursor, true, fScriptChecks, scriptFlags)) { return false; } @@ -743,7 +741,7 @@ namespace Novacoin if (!block.IsProofOfStake) { - ulong nBlockReward = CBlock.GetProofOfWorkReward(cursor.nBits, nFees); + long nBlockReward = CBlock.GetProofOfWorkReward(cursor.nBits, nFees); // Check coinbase reward if (block.vtx[0].nValueOut > nBlockReward) @@ -752,8 +750,8 @@ namespace Novacoin } } - cursor.nMint = (long)(nValueOut - nValueIn + nFees); - cursor.nMoneySupply = (cursor.prev != null ? cursor.prev.nMoneySupply : 0) + (long)nValueOut - (long)nValueIn; + cursor.nMint = nValueOut - nValueIn + nFees; + cursor.nMoneySupply = (cursor.prev != null ? cursor.prev.nMoneySupply : 0) + nValueOut - nValueIn; if (!UpdateDBCursor(ref cursor)) { @@ -853,15 +851,15 @@ namespace Novacoin return true; } - private bool ConnectInputs(CTransaction tx, ref Dictionary inputs, ref Dictionary queued, ref CBlockStoreItem cursorBlock, bool fBlock, bool fScriptChecks, scriptflag scriptFlags) + private bool ConnectInputs(ref CTransaction tx, ref Dictionary inputs, ref Dictionary queued, ref CBlockStoreItem cursorBlock, bool fBlock, bool fScriptChecks, scriptflag scriptFlags) { // Take over previous transactions' spent items // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain if (!tx.IsCoinBase) { - ulong nValueIn = 0; - ulong nFees = 0; + long nValueIn = 0; + long nFees = 0; for (uint i = 0; i < tx.vin.Length; i++) { var prevout = tx.vin[i].prevout; @@ -968,15 +966,15 @@ namespace Novacoin if (tx.IsCoinStake) { // ppcoin: coin stake tx earns reward instead of paying fee - ulong nCoinAge; + long nCoinAge; if (!tx.GetCoinAge(ref inputs, out nCoinAge)) { return false; // unable to get coin age for coinstake } - ulong nReward = tx.nValueOut - nValueIn; + long nReward = tx.nValueOut - nValueIn; - ulong nCalculatedReward = CBlock.GetProofOfStakeReward(nCoinAge, cursorBlock.nBits, tx.nTime) - tx.GetMinFee(1, false, CTransaction.MinFeeMode.GMF_BLOCK) + CTransaction.nCent; + long nCalculatedReward = CBlock.GetProofOfStakeReward(nCoinAge, cursorBlock.nBits, tx.nTime) - tx.GetMinFee(1, false, CTransaction.MinFeeMode.GMF_BLOCK) + CTransaction.nCent; if (nReward > nCalculatedReward) { @@ -991,7 +989,7 @@ namespace Novacoin } // Tally transaction fees - ulong nTxFee = nValueIn - tx.nValueOut; + long nTxFee = nValueIn - tx.nValueOut; if (nTxFee < 0) { return false; // nTxFee < 0 @@ -1031,7 +1029,7 @@ namespace Novacoin /// Block hash /// Proof-of-stake hash /// Proof-of-Stake hash value - private bool GetProofOfStakeHash(uint256 blockHash, out uint256 hashProofOfStake) + private bool GetProofOfStakeHash(ref uint256 blockHash, out uint256 hashProofOfStake) { return mapProofOfStake.TryGetValue(blockHash, out hashProofOfStake); } @@ -1046,7 +1044,7 @@ namespace Novacoin return false; } - CBlockStoreItem prevBlockCursor = null; + CBlockStoreItem prevBlockCursor; if (!blockMap.TryGetValue(block.header.prevHash, out prevBlockCursor)) { // Unable to get the cursor. @@ -1074,6 +1072,12 @@ namespace Novacoin } } + // Check that the block chain matches the known block chain up to a checkpoint + if (!Checkpoints.Verify(nHeight, nHash)) + { + return false; // rejected by checkpoint lock-in + } + // TODO: Enforce rule that the coinbase starts with serialized block height // Write block to file. @@ -1283,12 +1287,6 @@ namespace Novacoin if (block.IsProofOfStake) { - if (!block.SignatureOK) - { - // Proof-of-Stake signature validation failure. - return false; - } - // TODO: proof-of-stake validation uint256 hashProofOfStake = 0, targetProofOfStake = 0; @@ -1311,12 +1309,22 @@ namespace Novacoin { if (block.IsProofOfStake) { - // TODO: limit duplicity on stake + var proof = block.ProofOfStake; + + // Limited duplicity on stake: prevents block flood attack + // Duplicate stake allowed only when there is orphan child block + if (mapStakeSeenOrphan.ContainsKey(proof) && !orphanMapByPrev.ContainsKey(blockHash)) + { + return false; // duplicate proof-of-stake + } + else + { + mapStakeSeenOrphan.TryAdd(proof, blockHash); + } } - var block2 = new CBlock(block); - orphanMap.TryAdd(blockHash, block2); - orphanMapByPrev.TryAdd(blockHash, block2); + orphanMap.TryAdd(blockHash, block); + orphanMapByPrev.TryAdd(blockHash, block); return true; } @@ -1328,32 +1336,39 @@ namespace Novacoin return false; } - // Recursively process any orphan blocks that depended on this one - var orphansQueue = new List(); - orphansQueue.Add(blockHash); - - for (int i = 0; i < orphansQueue.Count; i++) + if (orphanMapByPrev.Count > 0) { - var hashPrev = orphansQueue[i]; + // Recursively process any orphan blocks that depended on this one + + var orphansQueue = new List(); + orphansQueue.Add(blockHash); - foreach (var pair in orphanMap) + for (int i = 0; i < orphansQueue.Count; i++) { - var orphanBlock = pair.Value; + var hashPrev = orphansQueue[i]; - if (orphanBlock.header.prevHash == blockHash) + foreach (var pair in orphanMapByPrev) { - if (AcceptBlock(ref orphanBlock)) + var orphanBlock = pair.Value; + + if (orphanBlock.header.prevHash == blockHash) { - orphansQueue.Add(pair.Key); - } + if (AcceptBlock(ref orphanBlock)) + { + orphansQueue.Add(pair.Key); + } + + CBlock dummy1; + orphanMap.TryRemove(pair.Key, out dummy1); - CBlock dummy1; - orphanMap.TryRemove(pair.Key, out dummy1); + uint256 dummyHash; + mapStakeSeenOrphan.TryRemove(orphanBlock.ProofOfStake, out dummyHash); + } } - } - CBlock dummy2; - orphanMap.TryRemove(hashPrev, out dummy2); + CBlock dummy2; + orphanMapByPrev.TryRemove(hashPrev, out dummy2); + } } return true; @@ -1411,15 +1426,7 @@ namespace Novacoin } int nCount = blockMap.Count; - Console.WriteLine("nCount={0}, Hash={1}, NumTx={2}, Time={3}", nCount, block.header.Hash, block.vtx.Length, DateTime.Now); // Commit on each 100th block - - /* - if (nCount % 100 == 0 && nCount != 0) - { - Console.WriteLine("Commit..."); - dbConn.Commit(); - dbConn.BeginTransaction(); - }*/ + Console.WriteLine("nCount={0}, Hash={1}, NumTx={2}, Time={3}", nCount, block.header.Hash, block.vtx.Length, DateTime.Now); } return true;