public TransactionVerboseModel(NBitcoin.Transaction trx, Network network, NBitcoin.ChainedBlock block = null, ChainedBlock tip = null) : base(trx) { if (trx != null) { this.txid = trx.GetHash().ToString(); this.size = trx.GetSerializedSize(); this.version = trx.Version; this.locktime = trx.LockTime; this.vin = trx.Inputs.Select(txin => new Vin(txin.PrevOut, txin.Sequence, txin.ScriptSig)).ToList(); int n = 0; this.vout = trx.Outputs.Select(txout => new Vout(n++, txout, network)).ToList(); if (block != null) { blockhash = block.HashBlock.ToString(); time = blocktime = Utils.DateTimeToUnixTime(block.Header.BlockTime); if (tip != null) { confirmations = tip.Height - block.Height + 1; } } } }
public bool TrySetTip(BlockHeader header, out ChainedBlock chainedHeader) { if (header == null) throw new ArgumentNullException("header"); chainedHeader = null; var prev = GetBlock(header.HashPrevBlock); if(prev == null) return false; chainedHeader = new ChainedBlock(header, header.GetHash(), GetBlock(header.HashPrevBlock)); SetTip(chainedHeader); return true; }
// (memory only) Sequencial id assigned to distinguish order in which blocks are received. //uint nSequenceId; public ChainedBlock(BlockHeader header,uint256 headerHash, ChainedBlock previous) { if(previous != null) { nHeight = previous.Height + 1; } this.pprev = previous; //this.nDataPos = pos; this.header = header; this.phashBlock = headerHash ?? header.GetHash(); if(previous == null) { if(header.HashPrevBlock != 0) throw new ArgumentException("Only the genesis block can have no previous block"); } else { if(previous.HashBlock != header.HashPrevBlock) throw new ArgumentException("The previous block has not the expected hash"); } }
// Stake Modifier (hash modifier of proof-of-stake): // The purpose of stake modifier is to prevent a txout (coin) owner from // computing future proof-of-stake generated by this txout at the time // of transaction confirmation. To meet kernel protocol, the txout // must hash with a future stake modifier to generate the proof. // Stake modifier consists of bits each of which is contributed from a // selected block of a given block group in the past. // The selection of a block is based on a hash of the block's proof-hash and // the previous stake modifier. // Stake modifier is recomputed at a fixed time interval instead of every // block. This is to make it difficult for an attacker to gain control of // additional bits in the stake modifier, even after generating a chain of // blocks. private static bool ComputeNextStakeModifier(StakeChain stakeChain, ChainBase chainIndex, ChainedBlock pindexPrev, out ulong nStakeModifier, out bool fGeneratedStakeModifier) { nStakeModifier = 0; fGeneratedStakeModifier = false; if (pindexPrev == null) { fGeneratedStakeModifier = true; return(true); // genesis block's modifier is 0 } // First find current stake modifier and its generation block time // if it's not old enough, return the same stake modifier long nModifierTime = 0; if (!GetLastStakeModifier(stakeChain, pindexPrev, out nStakeModifier, out nModifierTime)) { return(false); //error("ComputeNextStakeModifier: unable to get last modifier"); } //LogPrint("stakemodifier", "ComputeNextStakeModifier: prev modifier=0x%016x time=%s\n", nStakeModifier, DateTimeStrFormat(nModifierTime)); if (nModifierTime / ModifierInterval >= pindexPrev.Header.Time / ModifierInterval) { return(true); } // Sort candidate blocks by timestamp var sortedByTimestamp = new SortedDictionary <uint, uint256>(); long nSelectionInterval = GetStakeModifierSelectionInterval(); long nSelectionIntervalStart = (pindexPrev.Header.Time / ModifierInterval) * ModifierInterval - nSelectionInterval; var pindex = pindexPrev; while (pindex != null && pindex.Header.Time >= nSelectionIntervalStart) { sortedByTimestamp.Add(pindex.Header.Time, pindex.HashBlock); pindex = pindex.Previous; } int nHeightFirstCandidate = pindex?.Height + 1 ?? 0; // Select 64 blocks from candidate blocks to generate stake modifier ulong nStakeModifierNew = 0; long nSelectionIntervalStop = nSelectionIntervalStart; var mapSelectedBlocks = new Dictionary <uint256, ChainedBlock>(); for (int nRound = 0; nRound < Math.Min(64, sortedByTimestamp.Count); nRound++) { // add an interval section to the current selection round nSelectionIntervalStop += GetStakeModifierSelectionIntervalSection(nRound); // select a block from the candidates of current round if (!SelectBlockFromCandidates(stakeChain, pindexPrev, sortedByTimestamp, mapSelectedBlocks, nSelectionIntervalStop, nStakeModifier, out pindex)) { return(false); // error("ComputeNextStakeModifier: unable to select block at round %d", nRound); } // write the entropy bit of the selected block var blockStake = stakeChain.Get(pindex.HashBlock); nStakeModifierNew |= ((ulong)blockStake.GetStakeEntropyBit() << nRound); // add the selected block from candidates to selected list mapSelectedBlocks.Add(pindex.HashBlock, pindex); //LogPrint("stakemodifier", "ComputeNextStakeModifier: selected round %d stop=%s height=%d bit=%d\n", nRound, DateTimeStrFormat(nSelectionIntervalStop), pindex->nHeight, pindex->GetStakeEntropyBit()); } // // Print selection map for visualization of the selected blocks // if (LogAcceptCategory("stakemodifier")) // { // string strSelectionMap = ""; // '-' indicates proof-of-work blocks not selected // strSelectionMap.insert(0, pindexPrev->nHeight - nHeightFirstCandidate + 1, '-'); // pindex = pindexPrev; // while (pindex && pindex->nHeight >= nHeightFirstCandidate) // { // // '=' indicates proof-of-stake blocks not selected // if (pindex->IsProofOfStake()) // strSelectionMap.replace(pindex->nHeight - nHeightFirstCandidate, 1, "="); // pindex = pindex->pprev; // } // BOOST_FOREACH(const PAIRTYPE(uint256, const CBlockIndex*)& item, mapSelectedBlocks) // { // // 'S' indicates selected proof-of-stake blocks // // 'W' indicates selected proof-of-work blocks // strSelectionMap.replace(item.second->nHeight - nHeightFirstCandidate, 1, item.second->IsProofOfStake()? "S" : "W"); // } // LogPrintf("ComputeNextStakeModifier: selection height [%d, %d] map %s\n", nHeightFirstCandidate, pindexPrev->nHeight, strSelectionMap); // } //LogPrint("stakemodifier", "ComputeNextStakeModifier: new modifier=0x%016x time=%s\n", nStakeModifierNew, DateTimeStrFormat(pindexPrev->GetBlockTime())); nStakeModifier = nStakeModifierNew; fGeneratedStakeModifier = true; return(true); }
/// <summary> /// Gets the proof of work target for this entry in the chain. /// </summary> /// <param name="consensus">Consensus rules to use for this computation.</param> /// <returns>The target proof of work.</returns> public Target GetWorkRequired(Consensus consensus) { // Genesis block. if (this.Height == 0) { return(consensus.PowLimit); } Target proofOfWorkLimit = consensus.PowLimit; ChainedBlock lastBlock = this.Previous; var height = this.Height; if (lastBlock == null) { return(proofOfWorkLimit); } // Only change once per interval. if ((height) % consensus.DifficultyAdjustmentInterval != 0) { if (consensus.PowAllowMinDifficultyBlocks) { // Special difficulty rule for testnet: // If the new block's timestamp is more than 2* 10 minutes // then allow mining of a min-difficulty block. if (this.Header.BlockTime > (lastBlock.Header.BlockTime + TimeSpan.FromTicks(consensus.PowTargetSpacing.Ticks * 2))) { return(proofOfWorkLimit); } // Return the last non-special-min-difficulty-rules-block. ChainedBlock chainedBlock = lastBlock; while ((chainedBlock.Previous != null) && ((chainedBlock.Height % consensus.DifficultyAdjustmentInterval) != 0) && (chainedBlock.Header.Bits == proofOfWorkLimit)) { chainedBlock = chainedBlock.Previous; } return(chainedBlock.Header.Bits); } return(lastBlock.Header.Bits); } long pastHeight = 0; if (consensus.LitecoinWorkCalculation) { long blocksToGoBack = consensus.DifficultyAdjustmentInterval - 1; if ((lastBlock.Height + 1) != consensus.DifficultyAdjustmentInterval) { blocksToGoBack = consensus.DifficultyAdjustmentInterval; } pastHeight = lastBlock.Height - blocksToGoBack; } else { // Go back by what we want to be 14 days worth of blocks. pastHeight = lastBlock.Height - (consensus.DifficultyAdjustmentInterval - 1); } ChainedBlock firstChainedBlock = this.EnumerateToGenesis().FirstOrDefault(o => o.Height == pastHeight); if (firstChainedBlock == null) { throw new NotSupportedException("Can only calculate work of a full chain"); } if (consensus.PowNoRetargeting) { return(lastBlock.Header.Bits); } // Limit adjustment step. TimeSpan actualTimespan = lastBlock.Header.BlockTime - firstChainedBlock.Header.BlockTime; if (actualTimespan < TimeSpan.FromTicks(consensus.PowTargetTimespan.Ticks / 4)) { actualTimespan = TimeSpan.FromTicks(consensus.PowTargetTimespan.Ticks / 4); } if (actualTimespan > TimeSpan.FromTicks(consensus.PowTargetTimespan.Ticks * 4)) { actualTimespan = TimeSpan.FromTicks(consensus.PowTargetTimespan.Ticks * 4); } // Retarget. BigInteger newTarget = lastBlock.Header.Bits.ToBigInteger(); newTarget = newTarget.Multiply(BigInteger.ValueOf((long)actualTimespan.TotalSeconds)); newTarget = newTarget.Divide(BigInteger.ValueOf((long)consensus.PowTargetTimespan.TotalSeconds)); var finalTarget = new Target(newTarget); if (finalTarget > proofOfWorkLimit) { finalTarget = proofOfWorkLimit; } return(finalTarget); }
public static Target GetNextTargetRequired(StakeChain stakeChain, ChainedBlock indexLast, Consensus consensus, bool proofOfStake) { // Genesis block if (indexLast == null) { return(consensus.PowLimit); } // find the last two blocks that correspond to the mining algo // (i.e if this is a POS block we need to find the last two POS blocks) var targetLimit = proofOfStake ? GetProofOfStakeLimit(consensus, indexLast.Height) : consensus.PowLimit.ToBigInteger(); // first block var pindexPrev = GetLastBlockIndex(stakeChain, indexLast, proofOfStake); if (pindexPrev.Previous == null) { return(new Target(targetLimit)); } // second block var pindexPrevPrev = GetLastBlockIndex(stakeChain, pindexPrev.Previous, proofOfStake); if (pindexPrevPrev.Previous == null) { return(new Target(targetLimit)); } int targetSpacing = GetTargetSpacing(indexLast.Height); int actualSpacing = (int)(pindexPrev.Header.Time - pindexPrevPrev.Header.Time); if (IsProtocolV1RetargetingFixed(indexLast.Height)) { if (actualSpacing < 0) { actualSpacing = targetSpacing; } } if (IsProtocolV3((int)indexLast.Header.Time)) { if (actualSpacing > targetSpacing * 10) { actualSpacing = targetSpacing * 10; } } // target change every block // retarget with exponential moving toward target spacing var targetTimespan = 16 * 60; // 16 mins var target = pindexPrev.Header.Bits.ToBigInteger(); int interval = targetTimespan / targetSpacing; target = target.Multiply(BigInteger.ValueOf(((interval - 1) * targetSpacing + actualSpacing + actualSpacing))); target = target.Divide(BigInteger.ValueOf(((interval + 1) * targetSpacing))); if (target.CompareTo(BigInteger.Zero) <= 0 || target.CompareTo(targetLimit) >= 1) { //if (target <= 0 || target > targetLimit) target = targetLimit; } return(new Target(target)); }
// Purple kernel protocol // coinstake must meet hash target according to the protocol: // kernel (input 0) must meet the formula // hash(nStakeModifier + txPrev.block.nTime + txPrev.nTime + txPrev.vout.hash + txPrev.vout.n + nTime) < bnTarget * nWeight // this ensures that the chance of getting a coinstake is proportional to the // amount of coins one owns. // The reason this hash is chosen is the following: // nStakeModifier: scrambles computation to make it very difficult to precompute // future proof-of-stake // txPrev.block.nTime: prevent nodes from guessing a good timestamp to // generate transaction for future advantage, // obsolete since v3 // txPrev.nTime: slightly scrambles computation // txPrev.vout.hash: hash of txPrev, to reduce the chance of nodes // generating coinstake at the same time // txPrev.vout.n: output number of txPrev, to reduce the chance of nodes // generating coinstake at the same time // nTime: current timestamp // block/tx hash should not be used here as they can be generated in vast // quantities so as to generate blocks faster, degrading the system back into // a proof-of-work situation. // private static bool CheckStakeKernelHashV2(ChainedBlock pindexPrev, uint nBits, uint nTimeBlockFrom, BlockStake prevBlockStake, Transaction txPrev, OutPoint prevout, uint nTimeTx, out uint256 hashProofOfStake, out uint256 targetProofOfStake, bool fPrintProofOfStake) { targetProofOfStake = null; hashProofOfStake = null; if (nTimeTx < txPrev.Time) // Transaction timestamp violation { return(false); //error("CheckStakeKernelHash() : nTime violation"); } // Base target var bnTarget = new Target(nBits).ToBigInteger(); // Weighted target var nValueIn = txPrev.Outputs[prevout.N].Value.Satoshi; var bnWeight = BigInteger.ValueOf(nValueIn); bnTarget = bnTarget.Multiply(bnWeight); // todo: investigate this issue, is the convertion to uint256 similar to the c++ implementation //targetProofOfStake = Target.ToUInt256(bnTarget); var nStakeModifier = prevBlockStake.StakeModifier; //pindexPrev.Header.BlockStake.StakeModifier; uint256 bnStakeModifierV2 = prevBlockStake.StakeModifierV2; //pindexPrev.Header.BlockStake.StakeModifierV2; int nStakeModifierHeight = pindexPrev.Height; var nStakeModifierTime = pindexPrev.Header.Time; // Calculate hash using (var ms = new MemoryStream()) { var serializer = new BitcoinStream(ms, true); if (IsProtocolV3((int)nTimeTx)) { serializer.ReadWrite(bnStakeModifierV2); } else { serializer.ReadWrite(nStakeModifier); serializer.ReadWrite(nTimeBlockFrom); } serializer.ReadWrite(txPrev.Time); serializer.ReadWrite(prevout.Hash); serializer.ReadWrite(prevout.N); serializer.ReadWrite(nTimeTx); hashProofOfStake = Hashes.Hash256(ms.ToArray()); } if (fPrintProofOfStake) { //LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from timestamp=%s\n", // nStakeModifier, nStakeModifierHeight, // DateTimeStrFormat(nStakeModifierTime), // DateTimeStrFormat(nTimeBlockFrom)); //LogPrintf("CheckStakeKernelHash() : check modifier=0x%016x nTimeBlockFrom=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", // nStakeModifier, // nTimeBlockFrom, txPrev.nTime, prevout.n, nTimeTx, // hashProofOfStake.ToString()); } // Now check if proof-of-stake hash meets target protocol var hashProofOfStakeTarget = new BigInteger(hashProofOfStake.ToBytes(false)); if (hashProofOfStakeTarget.CompareTo(bnTarget) > 0) { return(false); } // if (fDebug && !fPrintProofOfStake) // { // LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from timestamp=%s\n", // nStakeModifier, nStakeModifierHeight, // DateTimeStrFormat(nStakeModifierTime), // DateTimeStrFormat(nTimeBlockFrom)); // LogPrintf("CheckStakeKernelHash() : pass modifier=0x%016x nTimeBlockFrom=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", // nStakeModifier, // nTimeBlockFrom, txPrev.nTime, prevout.n, nTimeTx, // hashProofOfStake.ToString()); // } return(true); }
public bool Contains(ChainedBlock blockIndex) { if(StartHeight <= blockIndex.Height && blockIndex.Height <= Height) { return vChain[blockIndex.Height - StartHeight] == blockIndex; } else return false; }
// ppcoin: total coin age spent in transaction, in the unit of coin-days. // Only those coins meeting minimum age requirement counts. As those // transactions not in main chain are not currently indexed so we // might not find out about their coin age. Older transactions are // guaranteed to be in main chain by sync-checkpoint. This rule is // introduced to help nodes establish a consistent view of the coin // age (trust score) of competing branches. public static bool GetCoinAge(INBitcoinBlockRepository blockStore, ITransactionRepository trasnactionStore, IBlockTransactionMapStore mapStore, Transaction trx, ChainedBlock pindexPrev, out ulong nCoinAge) { BigInteger bnCentSecond = BigInteger.Zero; // coin age in the unit of cent-seconds nCoinAge = 0; if (trx.IsCoinBase) { return(true); } foreach (var txin in trx.Inputs) { // First try finding the previous transaction in database Transaction txPrev = trasnactionStore.Get(txin.PrevOut.Hash); if (txPrev == null) { continue; // previous transaction not in main chain } if (trx.Time < txPrev.Time) { return(false); // Transaction timestamp violation } if (IsProtocolV3((int)trx.Time)) { int nSpendDepth = 0; if (IsConfirmedInNPrevBlocks(blockStore, txPrev, pindexPrev, StakeMinConfirmations - 1, ref nSpendDepth)) { //LogPrint("coinage", "coin age skip nSpendDepth=%d\n", nSpendDepth + 1); continue; // only count coins meeting min confirmations requirement } } else { // Read block header var block = blockStore.GetBlock(txPrev.GetHash()); if (block == null) { return(false); // unable to read block of previous transaction } if (block.Header.Time + StakeMinAge > trx.Time) { continue; // only count coins meeting min age requirement } } long nValueIn = txPrev.Outputs[txin.PrevOut.N].Value; var multiplier = BigInteger.ValueOf((trx.Time - txPrev.Time) / CENT); bnCentSecond = bnCentSecond.Add(BigInteger.ValueOf(nValueIn).Multiply(multiplier)); //bnCentSecond += new BigInteger(nValueIn) * (trx.Time - txPrev.Time) / CENT; //LogPrint("coinage", "coin age nValueIn=%d nTimeDiff=%d bnCentSecond=%s\n", nValueIn, nTime - txPrev.nTime, bnCentSecond.ToString()); } BigInteger bnCoinDay = bnCentSecond.Multiply(BigInteger.ValueOf(CENT / COIN / (24 * 60 * 60))); //BigInteger bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60); //LogPrint("coinage", "coin age bnCoinDay=%s\n", bnCoinDay.ToString()); nCoinAge = new Target(bnCoinDay).ToCompact(); return(true); }
public bool Contains(ChainedBlock blockIndex) { return(GetBlock(blockIndex.Height) != null); }
/// <summary> /// Force a new tip for the chain /// </summary> /// <param name="pindex"></param> /// <returns>forking point</returns> public override ChainedBlock SetTip(ChainedBlock block) { using(@lock.LockWrite()) { return SetTipNoLock(block); } }
/// <summary> /// Set time to consensus acceptable value /// </summary> /// <param name="network">Network</param> /// <param name="prev">previous block</param> public void UpdateTime(Network network, ChainedBlock prev) { var nOldTime = this.BlockTime; var mtp = prev.GetMedianTimePast() + TimeSpan.FromSeconds(1); var now = DateTimeOffset.UtcNow; var nNewTime = mtp > now ? mtp : now; if(nOldTime < nNewTime) this.BlockTime = nNewTime; // Updating time can change work required on testnet: if(network.Consensus.PowAllowMinDifficultyBlocks) Bits = GetWorkRequired(network, prev); }
public Target GetWorkRequired(Network network, ChainedBlock prev) { return new ChainedBlock(this, null, prev).GetWorkRequired(network); }
/// <summary> /// Change tip of the chain /// </summary> /// <param name="pindex"></param> /// <returns>forking point</returns> internal ChainedBlock SetTip(ChainedBlock pindex) { int backtrackCount = 0; foreach(var remove in vChain.Resize(pindex.Height + 1 - StartHeight)) { index.Remove(remove.HashBlock); offchainIndex.AddOrReplace(remove.HashBlock, remove); backtrackCount++; } while(pindex != null && vChain[pindex.Height - StartHeight] != pindex) { var old = vChain[pindex.Height - StartHeight]; if(old != null) { index.Remove(old.HashBlock); offchainIndex.AddOrReplace(old.HashBlock, old); backtrackCount++; } vChain[pindex.Height - StartHeight] = pindex; index.AddOrReplace(pindex.HashBlock, pindex); pindex = pindex.Previous; } if(backtrackCount != 0) Changes.WriteNext(new ChainChange() { Add = false, HeightOrBackstep = (uint)(backtrackCount) }); for(int i = pindex.Height + 1 ; i <= Tip.Height ; i++) { var block = vChain[i - StartHeight]; Changes.WriteNext(new ChainChange() { Add = true, BlockHeader = block.Header, HeightOrBackstep = (uint)block.Height }); } return pindex; }
public ChainedBlock GetOrAdd(BlockHeader header) { AssertInitialized(); var headerHash = header.GetHash(); ChainedBlock pindex = GetBlock(headerHash, true); if(pindex != null) return pindex; ChainedBlock previous = GetBlock(header.HashPrevBlock, true); if(previous == null) { return null; } pindex = new ChainedBlock(header, headerHash, previous); if(previous.HashBlock == Tip.HashBlock) { var change = new ChainChange() { Add = true, BlockHeader = pindex.Header, HeightOrBackstep = (uint)pindex.Height }; ProcessAndRecord(change, pindex.HashBlock); } else { if(pindex.Height <= Tip.Height) { offchainIndex.Add(pindex.HashBlock, pindex); } else { var fork = FindFork(pindex.EnumerateToGenesis().Select(c => c.HashBlock)); var change = new ChainChange() { Add = false, HeightOrBackstep = (uint)(Tip.Height - fork.Height) }; ProcessAndRecord(change, null); foreach(var block in pindex.EnumerateToGenesis().TakeWhile(s => s != fork).Reverse()) { change = new ChainChange() { Add = true, BlockHeader = block.Header, HeightOrBackstep = (uint)block.Height }; ProcessAndRecord(change, block.HashBlock); } } } return pindex; }
public IEnumerable<ChainedBlock> EnumerateAfter(ChainedBlock block) { for(int i = block.Height + 1 ; i < vChain.Count ; i++) { yield return vChain[i - StartHeight]; } }
public IEnumerable<ChainedBlock> EnumerateToTip(ChainedBlock block) { if(block == null) throw new ArgumentNullException("block"); return EnumerateToTip(block.HashBlock); }
public bool Contains(uint256 hash, bool includeBranch = false) { ChainedBlock pindex = GetBlock(hash, includeBranch); return(pindex != null); }
/// <summary> /// Force a new tip for the chain /// </summary> /// <param name="pindex"></param> /// <returns>forking point</returns> public abstract ChainedBlock SetTip(ChainedBlock pindex);
public Target GetWorkRequired(Network network, int height) { if (IsPartial) { throw new InvalidOperationException("You can't calculate work on partial chain"); } var nProofOfWorkLimit = new Target(network.ProofOfWorkLimit); var pindexLast = height == 0 ? null : GetBlock(height - 1); // Genesis block if (pindexLast == null) { return(nProofOfWorkLimit); } // Only change once per interval if ((height) % nInterval != 0) { if (network == Network.TestNet) { // Special difficulty rule for testnet: // If the new block's timestamp is more than 2* 10 minutes // then allow mining of a min-difficulty block. if (DateTimeOffset.UtcNow > pindexLast.Header.BlockTime + TimeSpan.FromTicks(nTargetSpacing.Ticks * 2)) { return(nProofOfWorkLimit); } else { // Return the last non-special-min-difficulty-rules-block ChainedBlock pindex = pindexLast; while (pindex.Previous != null && (pindex.Height % nInterval) != 0 && pindex.Header.Bits == nProofOfWorkLimit) { pindex = pindex.Previous; } return(pindex.Header.Bits); } } return(pindexLast.Header.Bits); } // Go back by what we want to be 14 days worth of blocks var pastHeight = pindexLast.Height - nInterval + 1; ChainedBlock pindexFirst = GetBlock((int)pastHeight); assert(pindexFirst); // Limit adjustment step var nActualTimespan = pindexLast.Header.BlockTime - pindexFirst.Header.BlockTime; if (nActualTimespan < TimeSpan.FromTicks(nTargetTimespan.Ticks / 4)) { nActualTimespan = TimeSpan.FromTicks(nTargetTimespan.Ticks / 4); } if (nActualTimespan > TimeSpan.FromTicks(nTargetTimespan.Ticks * 4)) { nActualTimespan = TimeSpan.FromTicks(nTargetTimespan.Ticks * 4); } // Retarget var bnNew = pindexLast.Header.Bits.ToBigInteger(); bnNew *= (ulong)nActualTimespan.TotalSeconds; bnNew /= (ulong)nTargetTimespan.TotalSeconds; var newTarget = new Target(bnNew); if (newTarget > nProofOfWorkLimit) { newTarget = nProofOfWorkLimit; } return(newTarget); }
private IEnumerable<ChainedBlock> EnumerateThisToFork(ChainedBlock block) { if(_Tip == null) yield break; var tip = _Tip; while(true) { if(object.ReferenceEquals(null, block) || object.ReferenceEquals(null, tip)) throw new InvalidOperationException("No fork found between the two chains"); if(tip.Height > block.Height) { yield return tip; tip = tip.Previous; } else if(tip.Height < block.Height) { block = block.Previous; } else if(tip.Height == block.Height) { if(tip.HashBlock == block.HashBlock) break; yield return tip; block = block.Previous; tip = tip.Previous; } } }
/// <summary> /// Whether the chain contains a chained block header with the given hash. /// </summary> /// <param name="hash">The hash to search for.</param> /// <returns>Whether the chain contains the chained block header.</returns> public bool Contains(uint256 hash) { ChainedBlock block = this.GetBlock(hash); return(block != null); }
public Target GetWorkRequired(Consensus consensus, ChainedBlock prev) { return(new ChainedBlock(this, null, prev).GetWorkRequired(consensus)); }
/// <summary> /// Force a new tip for the chain. /// </summary> /// <param name="chainedBlock">New tip for the chain.</param> /// <returns>Forking point.</returns> public abstract ChainedBlock SetTip(ChainedBlock chainedBlock);
// miner's coin stake reward public static long GetProofOfStakeReward(ChainedBlock pindexPrev, ulong nCoinAge, long nFees) { return(1 * BlockValidator.COIN + nFees); }
public bool Evaluate(ChainedBlock block) { var nBlockTime = block.Previous == null?Utils.UnixTimeToDateTime(0) : block.Previous.GetMedianTimePast(); return(this.MinHeight < block.Height && this.MinTime < nBlockTime); }
private static bool IsConfirmedInNPrevBlocks(INBitcoinBlockRepository blockStore, Transaction txPrev, ChainedBlock pindexFrom, int maxDepth, ref int actualDepth) { // note: this method can be optimized by ensuring that blockstore keeps // in memory at least maxDepth blocks twards genesis var hashPrev = txPrev.GetHash(); for (var pindex = pindexFrom; pindex != null && pindexFrom.Height - pindex.Height < maxDepth; pindex = pindex.Previous) { var block = blockStore.GetBlock(pindex.HashBlock); if (block == null) { return(false); } if (block.Transactions.Any(b => b.GetHash() == hashPrev)) //pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) { actualDepth = pindexFrom.Height - pindex.Height; return(true); } } return(false); }
public Target GetWorkRequired(Network network, ChainedBlock prev) { return(new ChainedBlock(this, null, prev).GetWorkRequired(network)); }
// select a block from the candidate blocks in vSortedByTimestamp, excluding // already selected blocks in vSelectedBlocks, and with timestamp up to // nSelectionIntervalStop. private static bool SelectBlockFromCandidates(StakeChain stakeChain, ChainedBlock chainIndex, SortedDictionary <uint, uint256> sortedByTimestamp, Dictionary <uint256, ChainedBlock> mapSelectedBlocks, long nSelectionIntervalStop, ulong nStakeModifierPrev, out ChainedBlock pindexSelected) { bool fSelected = false; uint256 hashBest = 0; pindexSelected = null; foreach (var item in sortedByTimestamp) { var pindex = chainIndex.FindAncestorOrSelf(item.Value); if (pindex == null) { return(false); // error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString()); } if (fSelected && pindex.Header.Time > nSelectionIntervalStop) { break; } if (mapSelectedBlocks.Keys.Any(key => key == pindex.HashBlock)) { continue; } var blockStake = stakeChain.Get(pindex.HashBlock); // compute the selection hash by hashing its proof-hash and the // previous proof-of-stake modifier uint256 hashSelection; using (var ms = new MemoryStream()) { var serializer = new BitcoinStream(ms, true); serializer.ReadWrite(blockStake.HashProof); serializer.ReadWrite(nStakeModifierPrev); hashSelection = Hashes.Hash256(ms.ToArray()); } // the selection hash is divided by 2**32 so that proof-of-stake block // is always favored over proof-of-work block. this is to preserve // the energy efficiency property if (blockStake.IsProofOfStake()) { hashSelection >>= 32; } if (fSelected && hashSelection < hashBest) { hashBest = hashSelection; pindexSelected = pindex; } else if (!fSelected) { fSelected = true; hashBest = hashSelection; pindexSelected = pindex; } } //LogPrint("stakemodifier", "SelectBlockFromCandidates: selection hash=%s\n", hashBest.ToString()); return(fSelected); }
public Target GetWorkRequired(Consensus consensus) { AssertHasHeader(); // Genesis block if (Height == 0) { return(consensus.PowLimit); } var nProofOfWorkLimit = consensus.PowLimit; var pindexLast = this.Previous; var height = Height; if (pindexLast == null) { return(nProofOfWorkLimit); } // Only change once per interval if ((height) % consensus.DifficultyAdjustmentInterval != 0) { if (consensus.PowAllowMinDifficultyBlocks) { // Special difficulty rule for testnet: // If the new block's timestamp is more than 2* 10 minutes // then allow mining of a min-difficulty block. if (this.Header.BlockTime > pindexLast.Header.BlockTime + TimeSpan.FromTicks(consensus.PowTargetSpacing.Ticks * 2)) { return(nProofOfWorkLimit); } else { // Return the last non-special-min-difficulty-rules-block ChainedBlock pindex = pindexLast; while (pindex.Previous != null && (pindex.Height % consensus.DifficultyAdjustmentInterval) != 0 && pindex.Header.Bits == nProofOfWorkLimit) { pindex = pindex.Previous; } return(pindex.Header.Bits); } } return(pindexLast.Header.Bits); } long pastHeight = 0; if (consensus.LitecoinWorkCalculation) { long blockstogoback = consensus.DifficultyAdjustmentInterval - 1; if ((pindexLast.Height + 1) != consensus.DifficultyAdjustmentInterval) { blockstogoback = consensus.DifficultyAdjustmentInterval; } pastHeight = pindexLast.Height - blockstogoback; } else { // Go back by what we want to be 14 days worth of blocks pastHeight = pindexLast.Height - (consensus.DifficultyAdjustmentInterval - 1); } ChainedBlock pindexFirst = this.EnumerateToGenesis().FirstOrDefault(o => o.Height == pastHeight); assert(pindexFirst); if (consensus.PowNoRetargeting) { return(pindexLast.Header.Bits); } // Limit adjustment step var nActualTimespan = pindexLast.Header.BlockTime - pindexFirst.Header.BlockTime; if (nActualTimespan < TimeSpan.FromTicks(consensus.PowTargetTimespan.Ticks / 4)) { nActualTimespan = TimeSpan.FromTicks(consensus.PowTargetTimespan.Ticks / 4); } if (nActualTimespan > TimeSpan.FromTicks(consensus.PowTargetTimespan.Ticks * 4)) { nActualTimespan = TimeSpan.FromTicks(consensus.PowTargetTimespan.Ticks * 4); } // Retarget var bnNew = pindexLast.Header.Bits.ToBigInteger(); bnNew = bnNew.Multiply(BigInteger.ValueOf((long)nActualTimespan.TotalSeconds)); bnNew = bnNew.Divide(BigInteger.ValueOf((long)consensus.PowTargetTimespan.TotalSeconds)); var newTarget = new Target(bnNew); if (newTarget > nProofOfWorkLimit) { newTarget = nProofOfWorkLimit; } return(newTarget); }
public bool Contains(uint256 hash) { ChainedBlock pindex = GetBlock(hash); return(pindex != null); }
public bool Evaluate(ChainedBlock block) { var nBlockTime = block.Previous == null ? Utils.UnixTimeToDateTime(0) : block.Previous.GetMedianTimePast(); return this.MinHeight < block.Height && this.MinTime < nBlockTime; }
/// <summary> /// Set time to consensus acceptable value /// </summary> /// <param name="network">Network</param> /// <param name="prev">previous block</param> public void UpdateTime(Network network, ChainedBlock prev) { UpdateTime(DateTimeOffset.UtcNow, network, prev); }
public virtual IEnumerable<ChainedBlock> EnumerateAfter(ChainedBlock block) { int i = block.Height + 1; var prev = block; while(true) { var b = GetBlock(i); if(b == null || b.Previous != prev) yield break; yield return b; i++; prev = b; } }
/// <summary> /// Set time to consensus acceptable value /// </summary> /// <param name="consensus">Consensus</param> /// <param name="prev">previous block</param> public void UpdateTime(Consensus consensus, ChainedBlock prev) { UpdateTime(DateTimeOffset.UtcNow, consensus, prev); }
public bool Contains(ChainedBlock blockIndex) { if(blockIndex == null) throw new ArgumentNullException("blockIndex"); return GetBlock(blockIndex.Height) != null; }
public IEnumerable<ChainBlockHeader> GetChainChangesUntilFork(ChainedBlock currentTip, bool forkIncluded, CancellationToken cancellation = default(CancellationToken)) { var oldTip = currentTip; var table = Configuration.GetChainTable(); List<ChainBlockHeader> blocks = new List<ChainBlockHeader>(); foreach(var chainPart in ExecuteBalanceQuery(table, new TableQuery(), new[] { 1, 2, 10 }) .Concat(table.ExecuteQuery(new TableQuery()).Skip(2)) .Select(e => new ChainPartEntry(e))) { cancellation.ThrowIfCancellationRequested(); int height = chainPart.ChainOffset + chainPart.BlockHeaders.Count - 1; foreach(var block in chainPart.BlockHeaders.Reverse<BlockHeader>()) { if(currentTip == null && oldTip != null) throw new InvalidOperationException("No fork found, the chain stored in azure is probably different from the one of the provided input"); if(oldTip == null || height > currentTip.Height) yield return CreateChainChange(height, block); else { if(height < currentTip.Height) currentTip = currentTip.FindAncestorOrSelf(height); var chainChange = CreateChainChange(height, block); if(chainChange.BlockId == currentTip.HashBlock) { if(forkIncluded) yield return chainChange; yield break; } yield return chainChange; currentTip = currentTip.Previous; } height--; } } }
private ChainedBlock SetTipNoLock(ChainedBlock block) { int height = Tip == null ? -1 : Tip.Height; foreach(var orphaned in EnumerateThisToFork(block)) { _BlocksById.Remove(orphaned.HashBlock); _BlocksByHeight.Remove(orphaned.Height); height--; } var fork = GetBlockNoLock(height); foreach(var newBlock in block.EnumerateToGenesis() .TakeWhile(c => c != Tip)) { _BlocksById.AddOrReplace(newBlock.HashBlock, newBlock); _BlocksByHeight.AddOrReplace(newBlock.Height, newBlock); } _Tip = block; return fork; }
public Chain CreateSubChain(ChainedBlock from, bool fromIncluded, ChainedBlock to, bool toIncluded, ObjectStream<ChainChange> output = null) { if(output == null) output = new StreamObjectStream<ChainChange>(); var blocks = to.EnumerateToGenesis() .Skip(toIncluded ? 0 : 1) .TakeWhile(c => c.HashBlock != from.HashBlock); if(fromIncluded) blocks = blocks.Concat(new ChainedBlock[] { from }); var array = blocks.Reverse().ToArray(); foreach(var b in array) { output.WriteNext(new ChainChange() { Add = true, BlockHeader = b.Header, HeightOrBackstep = (uint)b.Height }); } return new Chain(output); }
/// <summary> /// Set time to consensus acceptable value /// </summary> /// <param name="now">The expected date</param> /// <param name="network">Network</param> /// <param name="prev">previous block</param> public void UpdateTime(DateTimeOffset now, Network network, ChainedBlock prev) { UpdateTime(now, network.Consensus, prev); }
public Target GetWorkRequired(Network network, ChainedBlock prev) { return(GetWorkRequired(network.Consensus, prev)); }
public Target GetWorkRequired(Consensus consensus, ChainedBlock prev) { return(new ChainedBlock(this, this.GetHash(consensus.NetworkOptions), prev).GetWorkRequired(consensus)); }