public void TestBlockchainEquality() { var randomBlockchain = RandomData.RandomBlockchain(); var sameBlockchain = new Blockchain ( blockList: ImmutableList.Create(randomBlockchain.BlockList.ToArray()), blockListHashes: ImmutableHashSet.Create(randomBlockchain.BlockListHashes.ToArray()), utxo: randomBlockchain.Utxo.ToDictionary(x => x.Key, x => x.Value).ToImmutableDictionary(x => x.Key, x => x.Value) ); var newChainedBlock = randomBlockchain.BlockList.Last(); newChainedBlock = new ChainedBlock(newChainedBlock.BlockHash, newChainedBlock.PreviousBlockHash, newChainedBlock.Height + 1, newChainedBlock.TotalWork); var differentBlockchainBlockList = new Blockchain ( blockList: randomBlockchain.BlockList.Add(newChainedBlock), blockListHashes: randomBlockchain.BlockListHashes, utxo: randomBlockchain.Utxo ); var differentBlockchainBlockListHashes = new Blockchain ( blockList: randomBlockchain.BlockList, blockListHashes: randomBlockchain.BlockListHashes.Remove(randomBlockchain.BlockListHashes.Last()), utxo: randomBlockchain.Utxo ); var differentBlockchainUtxo = new Blockchain ( blockList: randomBlockchain.BlockList, blockListHashes: randomBlockchain.BlockListHashes, utxo: randomBlockchain.Utxo.Remove(randomBlockchain.Utxo.Keys.Last()) ); Assert.IsTrue(randomBlockchain.Equals(sameBlockchain)); Assert.IsTrue(randomBlockchain == sameBlockchain); Assert.IsFalse(randomBlockchain != sameBlockchain); Assert.IsFalse(randomBlockchain.Equals(differentBlockchainBlockList)); Assert.IsFalse(randomBlockchain == differentBlockchainBlockList); Assert.IsTrue(randomBlockchain != differentBlockchainBlockList); Assert.IsFalse(randomBlockchain.Equals(differentBlockchainBlockListHashes)); Assert.IsFalse(randomBlockchain == differentBlockchainBlockListHashes); Assert.IsTrue(randomBlockchain != differentBlockchainBlockListHashes); Assert.IsFalse(randomBlockchain.Equals(differentBlockchainUtxo)); Assert.IsFalse(randomBlockchain == differentBlockchainUtxo); Assert.IsTrue(randomBlockchain != differentBlockchainUtxo); }
public Operation(Transaction transaction, ChainedBlock block, MerkleBlock proof) : this() { Transaction = transaction; if(block != null) { proof = proof.Clone(); proof.PartialMerkleTree = proof.PartialMerkleTree.Trim(transaction.GetHash()); Height = block.Height; BlockId = block.HashBlock; Proof = proof; } UnconfirmedSeen = DateTimeOffset.UtcNow; AddedDate = DateTimeOffset.UtcNow; }
public static void UpdateChain(this IEnumerable<ChainBlockHeader> entries, ChainBase chain) { Stack<ChainBlockHeader> toApply = new Stack<ChainBlockHeader>(); foreach(var entry in entries) { var prev = chain.GetBlock(entry.Header.HashPrevBlock); if(prev == null) toApply.Push(entry); else { toApply.Push(entry); break; } } while(toApply.Count > 0) { var newTip = toApply.Pop(); var chained = new ChainedBlock(newTip.Header, newTip.BlockId, chain.GetBlock(newTip.Header.HashPrevBlock)); chain.SetTip(chained); } }
public ChainedBlock CreateBlockIndex(ChainedBlock prev) { ChainedBlock index = new ChainedBlock(new BlockHeader(), new BlockHeader().GetHash(), prev); return(index); }
public async Task DownloadAndStoreBlocks(CancellationToken token, bool disposemode = false) { // TODO: add support to BlockStoreLoop to unset LazyLoadingOn when not in IBD // When in IBD we may need many reads for the block key without fetching the block // So the repo starts with LazyLoadingOn = true, however when not anymore in IBD // a read is normally done when a peer is asking for the entire block (not just the key) // then if LazyLoadingOn = false the read will be faster on the entire block while (!token.IsCancellationRequested) { if (StoredBlock.Height >= this.ChainState.HighestValidatedPoW?.Height) { break; } // find next block to download var next = this.chain.GetBlock(StoredBlock.Height + 1); if (next == null) { break; //no blocks to store } // reorg logic if (this.StoredBlock.HashBlock != next.Header.HashPrevBlock) { if (disposemode) { break; } var blockstoremove = new List <uint256>(); var remove = this.StoredBlock; // reorg - we need to delete blocks, start walking back the chain while (this.chain.GetBlock(remove.HashBlock) == null) { blockstoremove.Add(remove.HashBlock); remove = remove.Previous; } await this.BlockRepository.DeleteAsync(remove.HashBlock, blockstoremove); this.StoredBlock = remove; this.ChainState.HighestPersistedBlock = this.StoredBlock; break; } if (await this.BlockRepository.ExistAsync(next.HashBlock)) { // next block is in storage update StoredBlock await this.BlockRepository.SetBlockHash(next.HashBlock); this.StoredBlock = next; this.ChainState.HighestPersistedBlock = this.StoredBlock; continue; } // check if the next block is in pending storage // then loop over the pending items and push to store in batches // if a stop condition is met break from the loop back to the start BlockPair insert; if (this.PendingStorage.TryGetValue(next.HashBlock, out insert)) { // if in IBD and batch is not full then wait for more blocks if (this.ChainState.IsInitialBlockDownload && !disposemode) { if (this.PendingStorage.Skip(0).Count() < batchtriggersize) // ConcurrentDictionary perf { break; } } if (!this.PendingStorage.TryRemove(next.HashBlock, out insert)) { break; } var tostore = new List <BlockPair>(new[] { insert }); var storebest = next; var insertSize = insert.Block.GetSerializedSize(); while (!token.IsCancellationRequested) { var old = next; next = this.chain.GetBlock(next.Height + 1); var stop = false; // stop if at the tip or block is already in store or pending insertion if (next == null) { stop = true; } else if (next.Header.HashPrevBlock != old.HashBlock) { stop = true; } else if (next.Height > this.ChainState.HighestValidatedPoW?.Height) { stop = true; } else if (!this.PendingStorage.TryRemove(next.HashBlock, out insert)) { stop = true; } if (stop) { if (!tostore.Any()) { break; } } else { tostore.Add(insert); storebest = next; insertSize += insert.Block.GetSerializedSize(); // TODO: add the size to the result coming from the signaler } if (insertSize > insertsizebyte || stop) { // store missing blocks and remove them from pending blocks await this.BlockRepository.PutAsync(storebest.HashBlock, tostore.Select(b => b.Block).ToList()); this.StoredBlock = storebest; this.ChainState.HighestPersistedBlock = this.StoredBlock; if (stop) { break; } tostore.Clear(); insertSize = 0; // this can be twicked if insert is effecting the consensus speed if (this.ChainState.IsInitialBlockDownload) { await Task.Delay(pushIntervalIBD, token); } } } continue; } if (disposemode) { break; } // continuously download blocks until a stop condition is found. // there are two operations, one is finding blocks to download // and asking them to the puller and the other is collecting // downloaded blocks and persisting them as a batch. var store = new List <BlockPair>(); var downloadStack = new Queue <ChainedBlock>(new[] { next }); this.blockPuller.AskBlock(next); int insertdownloadSize = 0; bool download = true; while (!token.IsCancellationRequested) { if (download) { var old = next; next = this.chain.GetBlock(old.Height + 1); var stop = false; // stop if at the tip or block is already in store or pending insertion if (next == null) { stop = true; } else if (next.Header.HashPrevBlock != old.HashBlock) { stop = true; } else if (next.Height > this.ChainState.HighestValidatedPoW?.Height) { stop = true; } else if (this.PendingStorage.ContainsKey(next.HashBlock)) { stop = true; } else if (await this.BlockRepository.ExistAsync(next.HashBlock)) { stop = true; } if (stop) { if (!downloadStack.Any()) { break; } download = false; } else { this.blockPuller.AskBlock(next); downloadStack.Enqueue(next); if (downloadStack.Count == batchdownloadsize) { download = false; } } } BlockPuller.DownloadedBlock block; if (this.blockPuller.TryGetBlock(downloadStack.Peek(), out block)) { var downloadbest = downloadStack.Dequeue(); store.Add(new BlockPair { Block = block.Block, ChainedBlock = downloadbest }); insertdownloadSize += block.Length; // can we push if (insertdownloadSize > insertsizebyte || !downloadStack.Any()) // this might go above the max insert size { await this.BlockRepository.PutAsync(downloadbest.HashBlock, store.Select(t => t.Block).ToList()); this.StoredBlock = downloadbest; this.ChainState.HighestPersistedBlock = this.StoredBlock; insertdownloadSize = 0; store.Clear(); if (!downloadStack.Any()) { break; } } } else { // waiting for blocks so sleep one await Task.Delay(100, token); } } } }
void Intercept(IncomingMessage message, Action act) { var inv = message.Message.Payload as InvPayload; if(inv != null) { if(inv.Inventory.Any(i => ((i.Type & InventoryType.MSG_BLOCK) != 0) && !Chain.Contains(i.Hash))) { _Refresh.Dispose(); //No need of periodical refresh, the peer is notifying us if(AutoSync) TrySync(); } } var getheaders = message.Message.Payload as GetHeadersPayload; if(getheaders != null && CanRespondToGetHeaders) { HeadersPayload headers = new HeadersPayload(); var fork = Chain.FindFork(getheaders.BlockLocators); if(fork != null) foreach(var header in Chain.EnumerateToTip(fork).Skip(1)) { headers.Headers.Add(header.Header); if(header.HashBlock == getheaders.HashStop || headers.Headers.Count == 2000) break; } AttachedNode.SendMessageAsync(headers); } var newheaders = message.Message.Payload as HeadersPayload; var pendingTipBefore = GetPendingTip(); if(newheaders != null && CanSync) { var tip = GetPendingTip(); foreach(var header in newheaders.Headers) { var prev = tip.FindAncestorOrSelf(header.HashPrevBlock); if(prev == null) break; tip = new ChainedBlock(header, header.GetHash(), prev); if(!AttachedNode.IsTrusted) { var validated = Chain.GetBlock(tip.HashBlock) != null || tip.Validate(AttachedNode.Network); if(!validated) { invalidHeaderReceived = true; break; } } _PendingTip = tip; } if(_PendingTip.Height > Chain.Tip.Height) { Chain.SetTip(_PendingTip); } var chainedPendingTip = Chain.GetBlock(_PendingTip.HashBlock); if(chainedPendingTip != null) { _PendingTip = chainedPendingTip; //This allows garbage collection to collect the duplicated pendingtip and ancestors } if(newheaders.Headers.Count != 0 && pendingTipBefore.HashBlock != GetPendingTip().HashBlock) TrySync(); Interlocked.Decrement(ref _SynchingCount); } act(); }
public static void IndexingChain(ChainedBlock from, ChainedBlock to) { _Trace.TraceInformation("Indexing blocks from " + ToString(from) + " to " + ToString(to) + " (both included)"); }
internal static void IndexedChainIsUpToDate(ChainedBlock block) { _Trace.TraceInformation("Indexed chain is up to date at height " + ToString(block)); }
public RuleContext(BlockValidationContext blockValidationContext, NBitcoin.Consensus consensus, ChainedBlock consensusTip) { Guard.NotNull(blockValidationContext, nameof(blockValidationContext)); Guard.NotNull(consensus, nameof(consensus)); this.BlockValidationContext = blockValidationContext; this.Consensus = consensus; this.ConsensusTip = consensusTip; // TODO: adding flags to determine the flow of logic is not ideal // a refator is in debate on moving to a consensus rules engine // this will remove the need for flags as validation will only use // the required rules (i.e if the check pow rule will be ommited form the flow) this.CheckPow = true; this.CheckMerkleRoot = true; }
public GetTxOutModel(UnspentOutputs unspentOutputs, uint vout, Network network, ChainedBlock tip) { if (unspentOutputs != null) { var output = unspentOutputs.TryGetOutput(vout); this.bestblock = tip.HashBlock; this.coinbase = unspentOutputs.IsCoinbase; this.confirmations = NetworkExtensions.MempoolHeight == unspentOutputs.Height ? 0 : tip.Height - (int)unspentOutputs.Height + 1; if (output != null) { this.value = output.Value; this.scriptPubKey = new ScriptPubKey(output.ScriptPubKey, network); } } }
public override ChainedBlock SetTip(ChainedBlock pindex) { throw new NotImplementedException(); }
private bool Notify(Transaction tx, MerkleBlock blk) { bool hit = false; if(blk == null) { hit = _Tracker.NotifyTransaction(tx); } else { var prev = _Chain.GetBlock(blk.Header.HashPrevBlock); if(prev != null) { var header = new ChainedBlock(blk.Header, null, prev); hit = _Tracker.NotifyTransaction(tx, header, blk); } else { hit = _Tracker.NotifyTransaction(tx); } } Interlocked.Increment(ref _TotalReceived); if(!hit) { Interlocked.Increment(ref _FalsePositiveCount); if(MaximumFalsePositiveRate != null && _TotalReceived > 100 && ActualFalsePostiveRate >= MaximumFalsePositiveRate.Value) { this.AttachedNode.DisconnectAsync("The actual false positive rate exceed MaximumFalsePositiveRate"); } } return hit; }
public void TestChainedBlockEquality() { var randomChainedBlock = RandomData.RandomChainedBlock(); var sameChainedBlock = new ChainedBlock ( blockHash: randomChainedBlock.BlockHash, previousBlockHash: randomChainedBlock.PreviousBlockHash, height: randomChainedBlock.Height, totalWork: randomChainedBlock.TotalWork ); var differentChainedBlockBlockHash = new ChainedBlock ( blockHash: ~randomChainedBlock.BlockHash, previousBlockHash: randomChainedBlock.PreviousBlockHash, height: randomChainedBlock.Height, totalWork: randomChainedBlock.TotalWork ); var differentChainedBlockPreviousBlockHash = new ChainedBlock ( blockHash: randomChainedBlock.BlockHash, previousBlockHash: ~randomChainedBlock.PreviousBlockHash, height: randomChainedBlock.Height, totalWork: randomChainedBlock.TotalWork ); var differentChainedBlockHeight = new ChainedBlock ( blockHash: randomChainedBlock.BlockHash, previousBlockHash: randomChainedBlock.PreviousBlockHash, height: ~randomChainedBlock.Height, totalWork: randomChainedBlock.TotalWork ); var differentChainedBlockTotalWork = new ChainedBlock ( blockHash: randomChainedBlock.BlockHash, previousBlockHash: randomChainedBlock.PreviousBlockHash, height: randomChainedBlock.Height, totalWork: ~randomChainedBlock.TotalWork ); Assert.IsTrue(randomChainedBlock.Equals(sameChainedBlock)); Assert.IsTrue(randomChainedBlock == sameChainedBlock); Assert.IsFalse(randomChainedBlock != sameChainedBlock); Assert.IsFalse(randomChainedBlock.Equals(differentChainedBlockBlockHash)); Assert.IsFalse(randomChainedBlock == differentChainedBlockBlockHash); Assert.IsTrue(randomChainedBlock != differentChainedBlockBlockHash); Assert.IsFalse(randomChainedBlock.Equals(differentChainedBlockPreviousBlockHash)); Assert.IsFalse(randomChainedBlock == differentChainedBlockPreviousBlockHash); Assert.IsTrue(randomChainedBlock != differentChainedBlockPreviousBlockHash); Assert.IsFalse(randomChainedBlock.Equals(differentChainedBlockHeight)); Assert.IsFalse(randomChainedBlock == differentChainedBlockHeight); Assert.IsTrue(randomChainedBlock != differentChainedBlockHeight); Assert.IsFalse(randomChainedBlock.Equals(differentChainedBlockTotalWork)); Assert.IsFalse(randomChainedBlock == differentChainedBlockTotalWork); Assert.IsTrue(randomChainedBlock != differentChainedBlockTotalWork); }
private Operation Received(TrackedScript match, IndexedTxOut txout, ChainedBlock block, MerkleBlock proof) { var operation = new Operation(txout.Transaction, block, proof, _TrackedScripts); SetUnconfirmedSeenIfPossible(txout.Transaction, block, operation); var coin = new Coin(txout); operation.ReceivedCoins.Add(Tuple.Create(coin, match.GetId())); bool merged = false; var returned = _Operations.AddOrUpdate(operation.GetId(), operation, (k, old) => old.Merge(operation, out merged)); var trackedOutpoint = new TrackedOutpoint() { Coin = coin, TrackedScriptId = match.GetId(), Filter = match.Filter }; _TrackedOutpoints.TryAdd(trackedOutpoint.GetId(), trackedOutpoint); return (operation == returned || merged) ? operation : null; }
private Operation Spent(TrackedScript metadata, IndexedTxIn txin, Coin coin, ChainedBlock block, MerkleBlock proof) { var operation = new Operation(txin.Transaction, block, proof, _TrackedScripts); operation.SpentCoins.Add(Tuple.Create(coin, metadata.GetId())); SetUnconfirmedSeenIfPossible(txin.Transaction, block, operation); bool merged = false; var returned = _Operations.AddOrUpdate(operation.GetId(), operation, (k, old) => old.Merge(operation, out merged)); return (operation == returned || merged) ? operation : null; }
internal static void CheckpointSaved(ChainedBlock block, string checkpointName) { _Trace.TraceInformation("Checkpoint " + checkpointName + " saved at " + ToString(block)); }
/// <inheritdoc /> public void ProcessBlock(Block block) { Guard.NotNull(block, nameof(block)); this.logger.LogTrace("({0}:'{1}')", nameof(block), block.GetHash()); ChainedBlock newTip = this.chain.GetBlock(block.GetHash()); if (newTip == null) { this.logger.LogTrace("(-)[NEW_TIP_REORG]"); return; } // If the new block's previous hash is the same as the // wallet hash then just pass the block to the manager. if (block.Header.HashPrevBlock != this.walletTip.HashBlock) { // If previous block does not match there might have // been a reorg, check if the wallet is still on the main chain. ChainedBlock inBestChain = this.chain.GetBlock(this.walletTip.HashBlock); if (inBestChain == null) { // The current wallet hash was not found on the main chain. // A reorg happened so bring the wallet back top the last known fork. ChainedBlock fork = this.walletTip; // We walk back the chained block object to find the fork. while (this.chain.GetBlock(fork.HashBlock) == null) { fork = fork.Previous; } this.logger.LogInformation("Reorg detected, going back from '{0}' to '{1}'.", this.walletTip, fork); this.walletManager.RemoveBlocks(fork); this.walletTip = fork; this.logger.LogTrace("Wallet tip set to '{0}'.", this.walletTip); } // The new tip can be ahead or behind the wallet. // If the new tip is ahead we try to bring the wallet up to the new tip. // If the new tip is behind we just check the wallet and the tip are in the same chain. if (newTip.Height > this.walletTip.Height) { ChainedBlock findTip = newTip.FindAncestorOrSelf(this.walletTip.HashBlock); if (findTip == null) { this.logger.LogTrace("(-)[NEW_TIP_AHEAD_NOT_IN_WALLET]"); return; } this.logger.LogTrace("Wallet tip '{0}' is behind the new tip '{1}'.", this.walletTip, newTip); // The wallet is falling behind we need to catch up. this.logger.LogWarning("New tip '{0}' is too far in advance, put the puller back.", newTip); this.blockNotification.SyncFrom(this.walletTip.HashBlock); return; } else { ChainedBlock findTip = this.walletTip.FindAncestorOrSelf(newTip.HashBlock); if (findTip == null) { this.logger.LogTrace("(-)[NEW_TIP_BEHIND_NOT_IN_WALLET]"); return; } this.logger.LogTrace("Wallet tip '{0}' is ahead or equal to the new tip '{1}'.", this.walletTip, newTip.HashBlock); } } else { this.logger.LogTrace("New block follows the previously known block '{0}'.", this.walletTip); } this.walletTip = newTip; this.walletManager.ProcessBlock(block, newTip); this.logger.LogTrace("(-)"); }
public void Return(BlockHeader header, int height) { _Return = new ChainedBlock(header, height); }
public void SetLocation(ChainedBlock tip) { Guard.NotNull(tip, nameof(tip)); _Location = tip; }
private bool Notify(Transaction tx, MerkleBlock blk) { bool hit = false; if(blk == null) { hit = _Tracker.NotifyTransaction(tx); } else { var prev = _Chain.GetBlock(blk.Header.HashPrevBlock); if(prev != null) { var header = new ChainedBlock(blk.Header, null, prev); hit = _Tracker.NotifyTransaction(tx, header, blk); } else { hit = _Tracker.NotifyTransaction(tx); } } Interlocked.Increment(ref _TotalReceived); if(!hit) { Interlocked.Increment(ref _FalsePositiveCount); } return hit; }
void Intercept(IncomingMessage message, Action act) { var inv = message.Message.Payload as InvPayload; if (inv != null) { if (inv.Inventory.Any(i => ((i.Type & InventoryType.MSG_BLOCK) != 0) && !Chain.Contains(i.Hash))) { _Refresh.Dispose(); //No need of periodical refresh, the peer is notifying us if (AutoSync) { TrySync(); } } } var getheaders = message.Message.Payload as GetHeadersPayload; if (getheaders != null && CanRespondToGetHeaders && !StripHeader) { HeadersPayload headers = new HeadersPayload(); var highestPow = SharedState.HighestValidatedPoW; highestPow = highestPow == null ? null : Chain.GetBlock(highestPow.HashBlock); var fork = Chain.FindFork(getheaders.BlockLocators); if (fork != null) { if (highestPow != null && fork.Height > highestPow.Height) { fork = null; //fork not yet validated } if (fork != null) { foreach (var header in Chain.EnumerateToTip(fork).Skip(1)) { if (highestPow != null && header.Height > highestPow.Height) { break; } headers.Headers.Add(header.Header); if (header.HashBlock == getheaders.HashStop || headers.Headers.Count == 2000) { break; } } } } AttachedNode.SendMessageAsync(headers); } var newheaders = message.Message.Payload as HeadersPayload; var pendingTipBefore = GetPendingTipOrChainTip(); if (newheaders != null && CanSync) { var tip = GetPendingTipOrChainTip(); foreach (var header in newheaders.Headers) { var prev = tip.FindAncestorOrSelf(header.HashPrevBlock); if (prev == null) { break; } tip = new ChainedBlock(header, header.GetHash(), prev); var validated = Chain.GetBlock(tip.HashBlock) != null || (SkipPoWCheck || tip.Validate(AttachedNode.Network)); validated &= !SharedState.IsMarkedInvalid(tip.HashBlock); if (!validated) { invalidHeaderReceived = true; break; } _PendingTip = tip; } bool isHigherBlock = false; if (SkipPoWCheck) { isHigherBlock = _PendingTip.Height > Chain.Tip.Height; } else { isHigherBlock = _PendingTip.GetChainWork(true) > Chain.Tip.GetChainWork(true); } if (isHigherBlock) { Chain.SetTip(_PendingTip); if (StripHeader) { _PendingTip.StripHeader(); } } var chainedPendingTip = Chain.GetBlock(_PendingTip.HashBlock); if (chainedPendingTip != null) { _PendingTip = chainedPendingTip; //This allows garbage collection to collect the duplicated pendingtip and ancestors } if (newheaders.Headers.Count != 0 && pendingTipBefore.HashBlock != GetPendingTipOrChainTip().HashBlock) { TrySync(); } Interlocked.Decrement(ref _SynchingCount); } act(); }
public bool NotifyTransaction(Transaction transaction, ChainedBlock chainedBlock, MerkleBlock proof) { if(chainedBlock != null) { if(proof == null) throw new ArgumentNullException("proof"); if(proof.Header.GetHash() != chainedBlock.Header.GetHash()) throw new InvalidOperationException("The chained block and the merkle block are different blocks"); if(!proof.PartialMerkleTree.Check(chainedBlock.Header.HashMerkleRoot)) throw new InvalidOperationException("The MerkleBlock does not have the expected merkle root"); if(!proof.PartialMerkleTree.GetMatchedTransactions().Contains(transaction.GetHash())) throw new InvalidOperationException("The MerkleBlock does not contains the input transaction"); } var interesting = false; lock(cs) { foreach(var txin in transaction.Inputs.AsIndexedInputs()) { var key = TrackedOutpoint.GetId(txin.PrevOut); TrackedOutpoint match; if(_TrackedOutpoints.TryGetValue(key, out match)) { TrackedScript parentMetadata; if(_TrackedScripts.TryGetValue(match.TrackedScriptId, out parentMetadata)) { interesting = true; Spent(parentMetadata, txin, match.Coin, chainedBlock, proof); } } } foreach(var txout in transaction.Outputs.AsIndexedOutputs()) { var key = TrackedScript.GetId(txout.TxOut.ScriptPubKey); TrackedScript match; if(_TrackedScripts.TryGetValue(key, out match)) { interesting = true; Received(match, txout, chainedBlock, proof); } } } return interesting; }
private ChainedBlock AppendBlock(params ConcurrentChain[] chains) { ChainedBlock index = null; return(AppendBlock(index, chains)); }
private static string ToString(ChainedBlock chainedBlock) { if (chainedBlock == null) return "(null)"; return ToString(chainedBlock.HashBlock, chainedBlock.Height); }
private void Notify(Transaction tx, MerkleBlock blk) { if(blk == null) { _Tracker.NotifyTransaction(tx); } else { var prev = _Chain.GetBlock(blk.Header.HashPrevBlock); if(prev != null) { var header = new ChainedBlock(blk.Header, null, prev); _Tracker.NotifyTransaction(tx, header, blk); } else { _Tracker.NotifyTransaction(tx); } } }
public virtual void CheckBlockReward(ContextInformation context, Money nFees, ChainedBlock chainedBlock, Block block) { Money blockReward = nFees + GetProofOfWorkReward(chainedBlock.Height); if (block.Transactions[0].TotalOut > blockReward) { ConsensusErrors.BadCoinbaseAmount.Throw(); } }
private void Received(TrackedScript match, IndexedTxOut txout, ChainedBlock block, MerkleBlock proof) { var operation = new Operation(txout.Transaction, block, proof); SetUnconfirmedSeenIfPossible(txout.Transaction, block, operation); var coin = new Coin(txout); operation.ReceivedCoins.Add(Tuple.Create(coin, match.GetId())); _Operations.AddOrUpdate(operation.GetId(), operation, (k, old) => old.Merge(operation)); var trackedOutpoint = new TrackedOutpoint() { Coin = coin, TrackedScriptId = match.GetId(), Filter = match.Filter }; _TrackedOutpoints.TryAdd(trackedOutpoint.GetId(), trackedOutpoint); }
public Testnet2Rules(CacheContext cacheContext) : base(cacheContext) { this._genesisBlock = new Block ( header: new BlockHeader ( version: 1, previousBlock: 0, merkleRoot: UInt256.Parse("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", NumberStyles.HexNumber), time: 1296688602, bits: 0x207FFFFF, nonce: 2 ), transactions: ImmutableArray.Create ( new Transaction ( version: 1, inputs: ImmutableArray.Create ( new TxInput ( previousTxOutputKey: new TxOutputKey ( txHash: 0, txOutputIndex: 0xFFFFFFFF ), scriptSignature: ImmutableArray.Create <byte> ( 0x04, 0xFF, 0xFF, 0x00, 0x1D, 0x01, 0x04, 0x45, 0x54, 0x68, 0x65, 0x20, 0x54, 0x69, 0x6D, 0x65, 0x73, 0x20, 0x30, 0x33, 0x2F, 0x4A, 0x61, 0x6E, 0x2F, 0x32, 0x30, 0x30, 0x39, 0x20, 0x43, 0x68, 0x61, 0x6E, 0x63, 0x65, 0x6C, 0x6C, 0x6F, 0x72, 0x20, 0x6F, 0x6E, 0x20, 0x62, 0x72, 0x69, 0x6E, 0x6B, 0x20, 0x6F, 0x66, 0x20, 0x73, 0x65, 0x63, 0x6F, 0x6E, 0x64, 0x20, 0x62, 0x61, 0x69, 0x6C, 0x6F, 0x75, 0x74, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x62, 0x61, 0x6E, 0x6B, 0x73 ), sequence: 0xFFFFFFFF ) ), outputs: ImmutableArray.Create ( new TxOutput ( value: (UInt64)(50L * 100.MILLION()), scriptPublicKey: ImmutableArray.Create <byte> ( 0x41, 0x04, 0x67, 0x8A, 0xFD, 0xB0, 0xFE, 0x55, 0x48, 0x27, 0x19, 0x67, 0xF1, 0xA6, 0x71, 0x30, 0xB7, 0x10, 0x5C, 0xD6, 0xA8, 0x28, 0xE0, 0x39, 0x09, 0xA6, 0x79, 0x62, 0xE0, 0xEA, 0x1F, 0x61, 0xDE, 0xB6, 0x49, 0xF6, 0xBC, 0x3F, 0x4C, 0xEF, 0x38, 0xC4, 0xF3, 0x55, 0x04, 0xE5, 0x1E, 0xC1, 0x12, 0xDE, 0x5C, 0x38, 0x4D, 0xF7, 0xBA, 0x0B, 0x8D, 0x57, 0x8A, 0x4C, 0x70, 0x2B, 0x6B, 0xF1, 0x1D, 0x5F, 0xAC ) ) ), lockTime: 0 ) ) ); Debug.Assert(_genesisBlock.Hash == UInt256.Parse("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206", NumberStyles.HexNumber)); this._genesisChainedBlock = new ChainedBlock ( blockHash: this._genesisBlock.Hash, previousBlockHash: this._genesisBlock.Header.PreviousBlock, height: 0, totalWork: this._genesisBlock.Header.CalculateWork() ); this._genesisBlockchain = new Data.Blockchain ( blockList: ImmutableList.Create(this._genesisChainedBlock), blockListHashes: ImmutableHashSet.Create(this._genesisBlock.Hash), utxo: ImmutableDictionary.Create <UInt256, UnspentTx>() // genesis block coinbase is not included in utxo, it is unspendable ); }
private ChainedBlock GetPendingTip() { _PendingTip = _PendingTip ?? Chain.Tip; return _PendingTip; }
public abstract BlockAssembler Create(ChainedBlock chainTip, AssemblerOptions options = null);
public override void SaveChanges(ChainedBlock newTip, IEnumerable <uint256> txIds, IEnumerable <Coins> coins) { _CurrentPrefetch = _PrefetchesByPrev.TryGet(newTip.HashBlock); _PrefetchesByPrev.Remove(newTip.HashBlock); _Inner.SaveChanges(newTip, txIds, coins); }
public override BlockAssembler Create(ChainedBlock chainTip, AssemblerOptions options = null) { return(new PowBlockAssembler(this.consensusLoop, this.network, this.mempoolLock, this.mempool, this.dateTimeProvider, chainTip, this.loggerFactory, options)); }
internal abstract Task <BlockStoreLoopStepResult> ExecuteAsync(ChainedBlock nextChainedBlock, CancellationToken cancellationToken, bool disposeMode);
private void Intercept(IncomingMessage message, Action continueInvocation) { this.logger.LogTrace("({0}:'{1}',{2}:'{3}')", nameof(message), message.Message.Command, nameof(this.AttachedNode), this.AttachedNode?.RemoteSocketEndpoint); var inv = message.Message.Payload as InvPayload; if (inv != null) { if (inv.Inventory.Any(i => ((i.Type & InventoryType.MSG_BLOCK) != 0) && !this.Chain.Contains(i.Hash))) { // No need of periodical refresh, the peer is notifying us. this.refreshTimer.Dispose(); if (this.AutoSync) { this.TrySync(); } } } // == GetHeadersPayload == // Represents our height from the peer's point of view. // It is sent from the peer on first connect, in response to Inv(Block) // or in response to HeaderPayload until an empty array is returned. // This payload notifies peers of our current best validated height. // Use the ChainState.ConsensusTip property (not Chain.Tip) // if the peer is behind/equal to our best height an empty array is sent back. // Ignoring "getheaders" from peers because node is in initial block download. var getheaders = message.Message.Payload as GetHeadersPayload; if ((getheaders != null) && this.CanRespondToGetHeaders // If not in IBD whitelisted won't be checked. && (!this.chainState.IsInitialBlockDownload || this.AttachedNode.Behavior <ConnectionManagerBehavior>().Whitelisted)) { HeadersPayload headers = new HeadersPayload(); ChainedBlock consensusTip = this.chainState.ConsensusTip; consensusTip = this.Chain.GetBlock(consensusTip.HashBlock); ChainedBlock fork = this.Chain.FindFork(getheaders.BlockLocators); if (fork != null) { if ((consensusTip == null) || (fork.Height > consensusTip.Height)) { // Fork not yet validated. fork = null; } if (fork != null) { foreach (ChainedBlock header in this.Chain.EnumerateToTip(fork).Skip(1)) { if (header.Height > consensusTip.Height) { break; } headers.Headers.Add(header.Header); if ((header.HashBlock == getheaders.HashStop) || (headers.Headers.Count == 2000)) { break; } } } } this.AttachedNode.SendMessageAsync(headers); } // == HeadersPayload == // Represents the peers height from our point view. // This updates the pending tip parameter which is // the peers current best validated height. // If the peer's height is higher Chain.Tip is updated to have // the most PoW header. // It is sent in response to GetHeadersPayload or is solicited by the // peer when a new block is validated (and not in IBD). var newHeaders = message.Message.Payload as HeadersPayload; if ((newHeaders != null) && this.CanSync) { ChainedBlock pendingTipBefore = this.GetPendingTipOrChainTip(); this.logger.LogTrace("Pending tip is '{0}', received {1} new headers.", pendingTipBefore, newHeaders.Headers.Count); // TODO: implement MAX_HEADERS_RESULTS in NBitcoin.HeadersPayload ChainedBlock tip = pendingTipBefore; foreach (BlockHeader header in newHeaders.Headers) { ChainedBlock prev = tip.FindAncestorOrSelf(header.HashPrevBlock); if (prev == null) { break; } tip = new ChainedBlock(header, header.GetHash(), prev); bool validated = this.Chain.GetBlock(tip.HashBlock) != null || tip.Validate(this.AttachedNode.Network); validated &= !this.chainState.IsMarkedInvalid(tip.HashBlock); if (!validated) { this.logger.LogTrace("Validation of new header '{0}' failed.", tip); this.invalidHeaderReceived = true; break; } this.pendingTip = tip; } if (pendingTipBefore != this.pendingTip) { this.logger.LogTrace("Pending tip changed to '{0}'.", this.pendingTip); } // Long reorganization protection on POS networks. bool reorgPrevented = false; uint maxReorgLength = this.chainState.MaxReorgLength; if (maxReorgLength != 0) { Network network = this.AttachedNode?.Network; ChainedBlock consensusTip = this.chainState.ConsensusTip; if ((network != null) && (consensusTip != null)) { ChainedBlock fork = this.pendingTip.FindFork(consensusTip); if ((fork != null) && (fork != consensusTip)) { int reorgLength = consensusTip.Height - fork.Height; if (reorgLength > maxReorgLength) { this.logger.LogTrace("Reorganization of length {0} prevented, maximal reorganization length is {1}, consensus tip is '{2}'.", reorgLength, maxReorgLength, consensusTip); this.invalidHeaderReceived = true; reorgPrevented = true; } else { this.logger.LogTrace("Reorganization of length {0} accepted, consensus tip is '{1}'.", reorgLength, consensusTip); } } } } if (!reorgPrevented && (this.pendingTip.ChainWork > this.Chain.Tip.ChainWork)) { this.logger.LogTrace("New chain tip '{0}' selected, chain work is '{1}'.", this.pendingTip, this.pendingTip.ChainWork); this.Chain.SetTip(this.pendingTip); } ChainedBlock chainedPendingTip = this.Chain.GetBlock(this.pendingTip.HashBlock); if (chainedPendingTip != null) { // This allows garbage collection to collect the duplicated pendingTip and ancestors. this.pendingTip = chainedPendingTip; } if ((!this.invalidHeaderReceived) && (newHeaders.Headers.Count != 0) && (pendingTipBefore.HashBlock != this.GetPendingTipOrChainTip().HashBlock)) { this.TrySync(); } } continueInvocation(); this.logger.LogTrace("(-)"); }
/// <summary> /// Initialize the BlockStore /// <para> /// If StoreTip is <c>null</c>, the store is out of sync. This can happen when:</para> /// <list> /// <item>1. The node crashed.</item> /// <item>2. The node was not closed down properly.</item> /// </list> /// <para> /// To recover we walk back the chain until a common block header is found /// and set the BlockStore's StoreTip to that. /// </para> /// </summary> public async Task Initialize() { this.logger.LogTrace("()"); if (this.nodeArgs.Store.ReIndex) { throw new NotImplementedException(); } this.StoreTip = this.Chain.GetBlock(this.BlockRepository.BlockHash); if (this.StoreTip == null) { var blockStoreResetList = new List <uint256>(); Block resetBlock = await this.BlockRepository.GetAsync(this.BlockRepository.BlockHash); uint256 resetBlockHash = resetBlock.GetHash(); while (this.Chain.GetBlock(resetBlockHash) == null) { blockStoreResetList.Add(resetBlockHash); if (resetBlock.Header.HashPrevBlock == this.Chain.Genesis.HashBlock) { resetBlockHash = this.Chain.Genesis.HashBlock; break; } resetBlock = await this.BlockRepository.GetAsync(resetBlock.Header.HashPrevBlock); Guard.NotNull(resetBlock, nameof(resetBlock)); resetBlockHash = resetBlock.GetHash(); } ChainedBlock newTip = this.Chain.GetBlock(resetBlockHash); await this.BlockRepository.DeleteAsync(newTip.HashBlock, blockStoreResetList); this.StoreTip = newTip; this.logger.LogWarning("{0} Initialize recovering to block height = {1}, hash = {2}.", this.StoreName, newTip.Height, newTip.HashBlock); } if (this.nodeArgs.Store.TxIndex != this.BlockRepository.TxIndex) { if (this.StoreTip != this.Chain.Genesis) { throw new BlockStoreException($"You need to rebuild the {this.StoreName} database using -reindex-chainstate to change -txindex"); } if (this.nodeArgs.Store.TxIndex) { await this.BlockRepository.SetTxIndex(this.nodeArgs.Store.TxIndex); } } SetHighestPersistedBlock(this.StoreTip); this.stepChain = new BlockStoreStepChain(); this.stepChain.SetNextStep(new ReorganiseBlockRepositoryStep(this, this.loggerFactory)); this.stepChain.SetNextStep(new CheckNextChainedBlockExistStep(this, this.loggerFactory)); this.stepChain.SetNextStep(new ProcessPendingStorageStep(this, this.loggerFactory)); this.stepChain.SetNextStep(new DownloadBlockStep(this, this.loggerFactory, this.dateTimeProvider)); StartLoop(); this.logger.LogTrace("(-)"); }
private ChainedBlock GetPendingTipOrChainTip() { this.pendingTip = this.pendingTip ?? this.chainState.ConsensusTip ?? this.Chain.Tip; return(this.pendingTip); }
/// <inheritdoc /> public void Start() { // subscribe to receiving blocks and transactions this.sub = this.signals.SubscribeForBlocks(new BlockObserver(this)); this.txSub = this.signals.SubscribeForTransactions(new TransactionObserver(this)); // if there is no wallet created yet, the wallet tip is the chain tip. if (!this.walletManager.ContainsWallets) { this.walletTip = this.chain.Tip; } else { this.walletTip = this.chain.GetBlock(this.walletManager.WalletTipHash); if (this.walletTip == null && this.chain.Height > 0) { // the wallet tip was not found in the main chain. // this can happen if the node crashes unexpectedly. // to recover we need to find the first common fork // with the best chain, as the wallet does not have a // list of chain headers we use a BlockLocator and persist // that in the wallet. the block locator will help finding // a common fork and bringing the wallet back to a good // state (behind the best chain) ICollection <uint256> locators = this.walletManager.GetFirstWalletBlockLocator(); BlockLocator blockLocator = new BlockLocator { Blocks = locators.ToList() }; ChainedBlock fork = this.chain.FindFork(blockLocator); this.walletManager.RemoveBlocks(fork); this.walletManager.WalletTipHash = fork.HashBlock; this.walletTip = fork; this.logger.LogWarning($"Wallet tip was out of sync, wallet tip reverted back to Height = {this.walletTip.Height} hash = {this.walletTip.HashBlock}."); } // we're looking from where to start syncing the wallets. // we start by looking at the heights of the wallets and we start syncing from the oldest one (the smallest height). // if for some reason we can't find a height, we look at the creation date of the wallets and we start syncing from the earliest date. int?earliestWalletHeight = this.walletManager.GetEarliestWalletHeight(); if (earliestWalletHeight == null) { DateTimeOffset oldestWalletDate = this.walletManager.GetOldestWalletCreationTime(); if (oldestWalletDate > this.walletTip.Header.BlockTime) { oldestWalletDate = this.walletTip.Header.BlockTime; } this.SyncFromDate(oldestWalletDate.LocalDateTime); } else { // if we reorged and the fork point is before the earliest wallet height start to // sync from the fork point. if (earliestWalletHeight.Value > this.walletTip.Height) { earliestWalletHeight = this.walletTip.Height; } this.SyncFromHeight(earliestWalletHeight.Value); } } }
public Task AnnounceBlocks(List <uint256> blockHashesToAnnounce) { this.logger.LogTrace("({0}.{1}:{2})", nameof(blockHashesToAnnounce), nameof(blockHashesToAnnounce.Count), blockHashesToAnnounce?.Count); Guard.NotNull(blockHashesToAnnounce, nameof(blockHashesToAnnounce)); if (!blockHashesToAnnounce.Any()) { this.logger.LogTrace("(-)[NO_HASHES]"); return(Task.CompletedTask); } Node node = this.AttachedNode; if (node == null) { this.logger.LogTrace("(-)[NO_NODE]"); return(Task.CompletedTask); } bool revertToInv = ((!this.PreferHeaders && (!this.preferHeaderAndIDs || blockHashesToAnnounce.Count > 1)) || blockHashesToAnnounce.Count > MAX_BLOCKS_TO_ANNOUNCE); var headers = new List <BlockHeader>(); var inventoryBlockToSend = new List <uint256>(); var chainBehavior = node.Behavior <ChainHeadersBehavior>(); ChainedBlock bestIndex = null; if (!revertToInv) { bool foundStartingHeader = false; // Try to find first header that our peer doesn't have, and // then send all headers past that one. If we come across any // headers that aren't on chainActive, give up. foreach (var hash in blockHashesToAnnounce) { ChainedBlock chainedBlock = this.chain.GetBlock(hash); if (chainedBlock == null) { // Bail out if we reorged away from this block revertToInv = true; break; } bestIndex = chainedBlock; if (foundStartingHeader) { headers.Add(chainedBlock.Header); } else if (chainBehavior.PendingTip.GetAncestor(chainedBlock.Height) != null) { continue; } else if (chainBehavior.PendingTip.GetAncestor(chainedBlock.Previous.Height) != null) { // Peer doesn't have this header but they do have the prior one. // Start sending headers. foundStartingHeader = true; headers.Add(chainedBlock.Header); } else { // Peer doesn't have this header or the prior one -- nothing will // connect, so bail out. revertToInv = true; break; } } } if (!revertToInv && headers.Any()) { if ((headers.Count == 1) && this.preferHeaderAndIDs) { // TODO: } else if (this.PreferHeaders) { if (headers.Count > 1) { this.logger.LogDebug("Sending {0} headers, range {1} - {2}, to peer '{3}'.", headers.Count, headers.First(), headers.Last(), node.RemoteSocketEndpoint); } else { this.logger.LogDebug("Sending header {0} to peer '{1}'.", headers.First(), node.RemoteSocketEndpoint); } chainBehavior.SetPendingTip(bestIndex); Task res = node.SendMessageAsync(new HeadersPayload(headers.ToArray())); this.logger.LogTrace("(-)[SEND_HEADERS_PAYLOAD]"); return(res); } else { revertToInv = true; } } if (revertToInv) { // If falling back to using an inv, just try to inv the tip. // The last entry in vBlockHashesToAnnounce was our tip at some point // in the past. if (blockHashesToAnnounce.Any()) { uint256 hashToAnnounce = blockHashesToAnnounce.Last(); ChainedBlock chainedBlock = this.chain.GetBlock(hashToAnnounce); if (chainedBlock != null) { if (chainBehavior.PendingTip.GetAncestor(chainedBlock.Height) == null) { inventoryBlockToSend.Add(hashToAnnounce); this.logger.LogDebug("Sending inventory hash '{0}' to peer '{1}'.", hashToAnnounce, node.RemoteSocketEndpoint); } } } } if (inventoryBlockToSend.Any()) { Task res = this.SendAsBlockInventoryAsync(node, inventoryBlockToSend); this.logger.LogTrace("(-)[SEND_INVENTORY]"); return(res); } this.logger.LogTrace("(-)"); return(Task.CompletedTask); }
public override void CheckBlockReward(ContextInformation context, Money nFees, ChainedBlock chainedBlock, Block block) { if (BlockStake.IsProofOfStake(block)) { // proof of stake invalidates previous inputs // and spends the inputs to new outputs with the // additional stake reward, next calculate the // reward does not exceed the consensus rules var stakeReward = block.Transactions[1].TotalOut - context.Stake.TotalCoinStakeValueIn; var calcStakeReward = nFees + GetProofOfStakeReward(chainedBlock.Height); if (stakeReward > calcStakeReward) { ConsensusErrors.BadCoinstakeAmount.Throw(); } } else { var blockReward = nFees + GetProofOfWorkReward(chainedBlock.Height); if (block.Transactions[0].TotalOut > blockReward) { ConsensusErrors.BadCoinbaseAmount.Throw(); } } }
public TransactionVerboseModel(Transaction trx, Network network, 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) { this.blockhash = block.HashBlock.ToString(); this.time = this.blocktime = Utils.DateTimeToUnixTime(block.Header.BlockTime); if (tip != null) { this.confirmations = tip.Height - block.Height + 1; } } } }
public override void SetLocation(ChainedBlock location) { _Location = location; }
public ChainedBlock AppendBlock(ChainedBlock previous, params ConcurrentChain[] chains) { ChainedBlock last = null; var nonce = RandomUtils.GetUInt32(); foreach(var chain in chains) { var block = TestUtils.CreateFakeBlock(new Transaction()); block.Header.HashPrevBlock = previous == null ? chain.Tip.HashBlock : previous.HashBlock; block.Header.Nonce = nonce; if(!chain.TrySetTip(block.Header, out last)) throw new InvalidOperationException("Previous not existing"); } return last; }
private ChainedBlock GetPendingTipOrChainTip() { _PendingTip = _PendingTip ?? Chain.Tip; return(_PendingTip); }
/// <summary> /// Calculates the difficulty target for the next block. /// </summary> /// <param name="stakeChain">Database of stake related data for the current blockchain.</param> /// <param name="chainedBlock">Block header for which to calculate the target difficulty.</param> /// <param name="consensus">Consensus rules for the current network.</param> /// <param name="proofOfStake"><c>true</c> for calculation of PoS difficulty target, <c>false</c> for calculation of PoW difficulty target.</param> /// <returns>The difficulty target for the next block after <paramref name="chainedBlock"/>.</returns> /// <remarks> /// The calculation of the next target is based on the last target value and the block time (aka spacing) of <paramref name="chainedBlock"/> /// (i.e. difference in time stamp of this block and its immediate predecessor). The target changes every block and it is adjusted /// down (i.e. towards harder to reach, or more difficult) if the time to mine last block was lower than the target block time. /// And it is adjusted up if it took longer than the target block time. The adjustments are done in a way the target is moving towards /// the target-spacing (expected block time) exponentially, so even a big change in the mining power on the network will be fixed by retargeting relatively quickly. /// <para> /// Over <see cref="RetargetIntervalMinutes"/> minutes there are certain number (say <c>N</c>) of blocks expected to be mined if the target block time /// of <see cref="TargetSpacingSeconds"/> was reached every time. Then the next target is calculated as follows:</para> /// <code> /// NewTarget = PrevTarget * ((N - 1) * TargetSpacingSeconds + 2 * LastBlockTime) / ((N + 1) * TargetSpacingSeconds) /// </code> /// <para> /// Which basically says that the block time of the last block is counted twice instead of two optimal block times. /// And the <c>N</c> determines how strongly will the deviation of the last block time affect the difficulty. /// </para> /// </remarks> public Target GetNextTargetRequired(StakeChain stakeChain, ChainedBlock chainedBlock, NBitcoin.Consensus consensus, bool proofOfStake) { this.logger.LogTrace("({0}:'{1}',{2}:{3})", nameof(chainedBlock), chainedBlock, nameof(proofOfStake), proofOfStake); // Genesis block. if (chainedBlock == null) { this.logger.LogTrace("(-)[GENESIS]:'{0}'", consensus.PowLimit); 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). BigInteger targetLimit = proofOfStake ? consensus.ProofOfStakeLimitV2 : consensus.PowLimit.ToBigInteger(); // First block. ChainedBlock lastPowPosBlock = GetLastPowPosChainedBlock(stakeChain, chainedBlock, proofOfStake); if (lastPowPosBlock.Previous == null) { var res = new Target(targetLimit); this.logger.LogTrace("(-)[FIRST_BLOCK]:'{0}'", res); return(res); } // Second block. ChainedBlock prevLastPowPosBlock = GetLastPowPosChainedBlock(stakeChain, lastPowPosBlock.Previous, proofOfStake); if (prevLastPowPosBlock.Previous == null) { var res = new Target(targetLimit); this.logger.LogTrace("(-)[SECOND_BLOCK]:'{0}'", res); return(res); } // This is used in tests to allow quickly mining blocks. if (consensus.PowNoRetargeting) { this.logger.LogTrace("(-)[NO_POW_RETARGET]:'{0}'", lastPowPosBlock.Header.Bits); return(lastPowPosBlock.Header.Bits); } int targetSpacing = TargetSpacingSeconds; int actualSpacing = (int)(lastPowPosBlock.Header.Time - prevLastPowPosBlock.Header.Time); if (actualSpacing < 0) { actualSpacing = targetSpacing; } if (actualSpacing > targetSpacing * 10) { actualSpacing = targetSpacing * 10; } int targetTimespan = RetargetIntervalMinutes * 60; int interval = targetTimespan / targetSpacing; BigInteger target = lastPowPosBlock.Header.Bits.ToBigInteger(); long multiplyBy = (interval - 1) * targetSpacing + actualSpacing + actualSpacing; target = target.Multiply(BigInteger.ValueOf(multiplyBy)); long divideBy = (interval + 1) * targetSpacing; target = target.Divide(BigInteger.ValueOf(divideBy)); this.logger.LogTrace("The next target difficulty will be {0} times higher (easier to satisfy) than the previous target.", (double)multiplyBy / (double)divideBy); if ((target.CompareTo(BigInteger.Zero) <= 0) || (target.CompareTo(targetLimit) >= 1)) { target = targetLimit; } var finalTarget = new Target(target); this.logger.LogTrace("(-):'{0}'", finalTarget); return(finalTarget); }
public List <uint256> GenerateBlocks(ReserveScript reserveScript, ulong generate, ulong maxTries) { ulong nHeightStart = 0; ulong nHeightEnd = 0; ulong nHeight = 0; nHeightStart = (ulong)this.chain.Height; nHeight = nHeightStart; nHeightEnd = nHeightStart + generate; int nExtraNonce = 0; var blocks = new List <uint256>(); while (nHeight < nHeightEnd) { this.nodeLifetime.ApplicationStopping.ThrowIfCancellationRequested(); ChainedBlock chainTip = this.consensusLoop.Tip; if (this.chain.Tip != chainTip) { Task.Delay(TimeSpan.FromMinutes(1), this.nodeLifetime.ApplicationStopping).GetAwaiter().GetResult(); continue; } BlockTemplate pblockTemplate = this.blockAssemblerFactory.Create(chainTip).CreateNewBlock(reserveScript.reserveSfullNodecript); if (Block.BlockSignature) { // POS: make sure the POS consensus rules are valid if (pblockTemplate.Block.Header.Time <= chainTip.Header.Time) { continue; } } this.IncrementExtraNonce(pblockTemplate.Block, chainTip, nExtraNonce); Block pblock = pblockTemplate.Block; while ((maxTries > 0) && (pblock.Header.Nonce < InnerLoopCount) && !pblock.CheckProofOfWork()) { this.nodeLifetime.ApplicationStopping.ThrowIfCancellationRequested(); ++pblock.Header.Nonce; --maxTries; } if (maxTries == 0) { break; } if (pblock.Header.Nonce == InnerLoopCount) { continue; } var newChain = new ChainedBlock(pblock.Header, pblock.GetHash(), chainTip); if (newChain.ChainWork <= chainTip.ChainWork) { continue; } var blockValidationContext = new BlockValidationContext { Block = pblock }; this.consensusLoop.AcceptBlock(blockValidationContext); if (blockValidationContext.ChainedBlock == null) { this.logger.LogTrace("(-)[REORG-2]"); return(blocks); } if (blockValidationContext.Error != null) { if (blockValidationContext.Error == ConsensusErrors.InvalidPrevTip) { continue; } this.logger.LogTrace("(-)[ACCEPT_BLOCK_ERROR]"); return(blocks); } this.logger.LogInformation("Mined new {0} block: '{1}'.", BlockStake.IsProofOfStake(blockValidationContext.Block) ? "POS" : "POW", blockValidationContext.ChainedBlock); nHeight++; blocks.Add(pblock.GetHash()); pblockTemplate = null; } return(blocks); }
public void Set(ChainedBlock chainedBlock, BlockStake blockStake) { this.SetAsync(chainedBlock, blockStake).GetAwaiter().GetResult(); }
private ChainedBlock AppendBlock(ChainedBlock previous, params Chain[] chains) { ChainedBlock last = null; var nonce = RandomUtils.GetUInt32(); foreach(var chain in chains) { var block = TestUtils.CreateFakeBlock(new Transaction()); block.Header.HashPrevBlock = previous == null ? chain.Tip.HashBlock : previous.HashBlock; block.Header.Nonce = nonce; last = chain.GetOrAdd(block.Header); } return last; }
public virtual void ExecuteBlock(ContextInformation context, TaskScheduler taskScheduler) { this.logger.LogTrace("()"); Block block = context.BlockValidationContext.Block; ChainedBlock index = context.BlockValidationContext.ChainedBlock; DeploymentFlags flags = context.Flags; UnspentOutputSet view = context.Set; this.PerformanceCounter.AddProcessedBlocks(1); taskScheduler = taskScheduler ?? TaskScheduler.Default; if (!context.BlockValidationContext.SkipValidation) { if (flags.EnforceBIP30) { foreach (Transaction tx in block.Transactions) { UnspentOutputs coins = view.AccessCoins(tx.GetHash()); if ((coins != null) && !coins.IsPrunable) { this.logger.LogTrace("(-)[BAD_TX_BIP_30]"); ConsensusErrors.BadTransactionBIP30.Throw(); } } } } else { this.logger.LogTrace("BIP30 validation skipped for checkpointed block at height {0}.", index.Height); } long nSigOpsCost = 0; Money nFees = Money.Zero; List <Task <bool> > checkInputs = new List <Task <bool> >(); for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++) { this.PerformanceCounter.AddProcessedTransactions(1); Transaction tx = block.Transactions[txIndex]; if (!context.BlockValidationContext.SkipValidation) { if (!tx.IsCoinBase && (!context.IsPoS || (context.IsPoS && !tx.IsCoinStake))) { int[] prevheights; if (!view.HaveInputs(tx)) { this.logger.LogTrace("(-)[BAD_TX_NO_INPUT]"); ConsensusErrors.BadTransactionMissingInput.Throw(); } prevheights = new int[tx.Inputs.Count]; // Check that transaction is BIP68 final. // BIP68 lock checks (as opposed to nLockTime checks) must // be in ConnectBlock because they require the UTXO set. for (int j = 0; j < tx.Inputs.Count; j++) { prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height; } if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags)) { this.logger.LogTrace("(-)[BAD_TX_NON_FINAL]"); ConsensusErrors.BadTransactionNonFinal.Throw(); } } // GetTransactionSigOpCost counts 3 types of sigops: // * legacy (always), // * p2sh (when P2SH enabled in flags and excludes coinbase), // * witness (when witness enabled in flags and excludes coinbase). nSigOpsCost += this.GetTransactionSigOpCost(tx, view, flags); if (nSigOpsCost > this.consensusOptions.MaxBlockSigopsCost) { ConsensusErrors.BadBlockSigOps.Throw(); } // TODO: Simplify this condition. if (!tx.IsCoinBase && (!context.IsPoS || (context.IsPoS && !tx.IsCoinStake))) { this.CheckInputs(tx, view, index.Height); nFees += view.GetValueIn(tx) - tx.TotalOut; Transaction localTx = tx; PrecomputedTransactionData txData = new PrecomputedTransactionData(tx); for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++) { this.PerformanceCounter.AddProcessedInputs(1); TxIn input = tx.Inputs[inputIndex]; int inputIndexCopy = inputIndex; TxOut txout = view.GetOutputFor(input); var checkInput = new Task <bool>(() => { if (this.UseConsensusLib) { Script.BitcoinConsensusError error; return(Script.VerifyScriptConsensus(txout.ScriptPubKey, tx, (uint)inputIndexCopy, flags.ScriptFlags, out error)); } else { var checker = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData); var ctx = new ScriptEvaluationContext(); ctx.ScriptVerify = flags.ScriptFlags; return(ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker)); } }); checkInput.Start(taskScheduler); checkInputs.Add(checkInput); } } } this.UpdateCoinView(context, tx); } if (!context.BlockValidationContext.SkipValidation) { this.CheckBlockReward(context, nFees, index, block); bool passed = checkInputs.All(c => c.GetAwaiter().GetResult()); if (!passed) { this.logger.LogTrace("(-)[BAD_TX_SCRIPT]"); ConsensusErrors.BadTransactionScriptError.Throw(); } } else { this.logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height); } this.logger.LogTrace("(-)"); }
public static string GetId(uint256 txId, ChainedBlock block) { return GetId(txId, block == null ? null : block.HashBlock, block == null ? 0 : block.Height); }
public virtual void CheckBlockReward(ContextInformation context, Money nFees, ChainedBlock chainedBlock, Block block) { this.logger.LogTrace("()"); Money blockReward = nFees + this.GetProofOfWorkReward(chainedBlock.Height); if (block.Transactions[0].TotalOut > blockReward) { this.logger.LogTrace("(-)[BAD_COINBASE_AMOUNT]"); ConsensusErrors.BadCoinbaseAmount.Throw(); } this.logger.LogTrace("(-)"); }
public bool NotifyTransaction(Transaction transaction, ChainedBlock chainedBlock, Block block) { if(chainedBlock == null) return NotifyTransaction(transaction); return NotifyTransaction(transaction, chainedBlock, new MerkleBlock(block, new uint256[] { transaction.GetHash() })); }
public IEnumerable <ChainedBlock> GetHeadersFromFork(NetworkPeer peer, ChainedBlock currentTip, uint256 hashStop = null, CancellationToken cancellationToken = default(CancellationToken)) { this.AssertStateAsync(peer, NetworkPeerState.HandShaked, cancellationToken).GetAwaiter().GetResult(); using (var listener = new NetworkPeerListener(peer)) { int acceptMaxReorgDepth = 0; while (true) { // Get before last so, at the end, we should only receive 1 header equals to this one (so we will not have race problems with concurrent GetChains). BlockLocator awaited = currentTip.Previous == null?currentTip.GetLocator() : currentTip.Previous.GetLocator(); peer.SendMessageVoidAsync(new GetHeadersPayload() { BlockLocators = awaited, HashStop = hashStop }); while (true) { bool isOurs = false; HeadersPayload headers = null; using (var headersCancel = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) { headersCancel.CancelAfter(TimeSpan.FromMinutes(1.0)); try { headers = listener.ReceivePayloadAsync <HeadersPayload>(headersCancel.Token).GetAwaiter().GetResult(); } catch (OperationCanceledException) { acceptMaxReorgDepth += 6; if (cancellationToken.IsCancellationRequested) { throw; } // Send a new GetHeaders. break; } } // In the special case where the remote node is at height 0 as well as us, then the headers count will be 0. if ((headers.Headers.Count == 0) && (peer.PeerVersion.StartHeight == 0) && (currentTip.HashBlock == peer.Network.GenesisHash)) { yield break; } if ((headers.Headers.Count == 1) && (headers.Headers[0].GetHash() == currentTip.HashBlock)) { yield break; } foreach (BlockHeader header in headers.Headers) { uint256 hash = header.GetHash(); if (hash == currentTip.HashBlock) { continue; } // The previous headers request timeout, this can arrive in case of big reorg. if (header.HashPrevBlock != currentTip.HashBlock) { int reorgDepth = 0; ChainedBlock tempCurrentTip = currentTip; while (reorgDepth != acceptMaxReorgDepth && tempCurrentTip != null && header.HashPrevBlock != tempCurrentTip.HashBlock) { reorgDepth++; tempCurrentTip = tempCurrentTip.Previous; } if (reorgDepth != acceptMaxReorgDepth && tempCurrentTip != null) { currentTip = tempCurrentTip; } } if (header.HashPrevBlock == currentTip.HashBlock) { isOurs = true; currentTip = new ChainedBlock(header, hash, currentTip); yield return(currentTip); if (currentTip.HashBlock == hashStop) { yield break; } } else { break; // Not our headers, continue receive. } } if (isOurs) { break; //Go ask for next header. } } } } }
private void Spent(TrackedScript metadata, IndexedTxIn txin, Coin coin, ChainedBlock block, MerkleBlock proof) { var operation = new Operation(txin.Transaction, block, proof); operation.SpentCoins.Add(Tuple.Create(coin, metadata.GetId())); SetUnconfirmedSeenIfPossible(txin.Transaction, block, operation); _Operations.AddOrUpdate(operation.GetId(), operation, (k, old) => old.Merge(operation)); }
public Block[] GenerateStratis(int blockCount, List <Transaction> passedTransactions = null, bool broadcast = true) { var fullNode = (this.runner as StratisBitcoinPowRunner).FullNode; BitcoinSecret dest = this.MinerSecret; List <Block> blocks = new List <Block>(); DateTimeOffset now = this.MockTime == null ? DateTimeOffset.UtcNow : this.MockTime.Value; #if !NOSOCKET for (int i = 0; i < blockCount; i++) { uint nonce = 0; Block block = new Block(); block.Header.HashPrevBlock = fullNode.Chain.Tip.HashBlock; block.Header.Bits = block.Header.GetWorkRequired(fullNode.Network, fullNode.Chain.Tip); block.Header.UpdateTime(now, fullNode.Network, fullNode.Chain.Tip); var coinbase = new Transaction(); coinbase.AddInput(TxIn.CreateCoinbase(fullNode.Chain.Height + 1)); coinbase.AddOutput(new TxOut(fullNode.Network.GetReward(fullNode.Chain.Height + 1), dest.GetAddress())); block.AddTransaction(coinbase); if (passedTransactions?.Any() ?? false) { passedTransactions = this.Reorder(passedTransactions); block.Transactions.AddRange(passedTransactions); } block.UpdateMerkleRoot(); while (!block.CheckProofOfWork(fullNode.Network.Consensus)) { block.Header.Nonce = ++nonce; } blocks.Add(block); if (broadcast) { uint256 blockHash = block.GetHash(); var newChain = new ChainedBlock(block.Header, blockHash, fullNode.Chain.Tip); var oldTip = fullNode.Chain.SetTip(newChain); fullNode.ConsensusLoop().Puller.InjectBlock(blockHash, new DownloadedBlock { Length = block.GetSerializedSize(), Block = block }, CancellationToken.None); //try //{ // var blockResult = new BlockResult { Block = block }; // fullNode.ConsensusLoop.AcceptBlock(blockResult); // // similar logic to what's in the full node code // if (blockResult.Error == null) // { // fullNode.ChainBehaviorState.ConsensusTip = fullNode.ConsensusLoop.Tip; // //if (fullNode.Chain.Tip.HashBlock == blockResult.ChainedBlock.HashBlock) // //{ // // var unused = cache.FlushAsync(); // //} // fullNode.Signals.Blocks.Broadcast(block); // } //} //catch (ConsensusErrorException) //{ // // set back the old tip // fullNode.Chain.SetTip(oldTip); //} } } return(blocks.ToArray()); #endif }
private void SetUnconfirmedSeenIfPossible(Transaction tx, ChainedBlock block, Operation operation) { if(block != null) { Operation unconf; if(_Operations.TryGetValue(Operation.GetId(tx.GetHash(), null), out unconf)) operation.UnconfirmedSeen = unconf.UnconfirmedSeen; } }
internal static void InputChainTip(ChainedBlock block) { _Trace.TraceInformation("The input chain tip is at height " + ToString(block)); }