private void OnNewHeaders(Header[] headers) { using (SnapshotView snapshot = GetSnapshot()) { foreach (Header header in headers) { if (header.Index - 1 >= header_index.Count) { break; } if (header.Index < header_index.Count) { continue; } if (!header.Verify(snapshot)) { break; } header_index.Add(header.Hash); snapshot.Blocks.Add(header.Hash, header.Trim()); snapshot.HeaderHashIndex.GetAndChange().Hash = header.Hash; snapshot.HeaderHashIndex.GetAndChange().Index = header.Index; } SaveHeaderHashList(snapshot); snapshot.Commit(); } UpdateCurrentSnapshot(); system.TaskManager.Tell(new TaskManager.HeaderTaskCompleted(), Sender); }
private void SendPingMessage() { if (sessions.Count == 0) { return; } TrimmedBlock block; using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) { block = snapshot.Blocks[snapshot.CurrentBlockHash]; } foreach (KeyValuePair <IActorRef, TaskSession> item in sessions) { var node = item.Key; var session = item.Value; if (session.ExpireTime < TimeProvider.Current.UtcNow || (block.Index >= session.LastBlockIndex && TimeProvider.Current.UtcNow.ToTimestampMS() - PingCoolingOffPeriod >= block.Timestamp)) { if (session.InvTasks.Remove(MemPoolTaskHash)) { node.Tell(Message.Create(MessageCommand.Mempool)); } node.Tell(Message.Create(MessageCommand.Ping, PingPayload.Create(Blockchain.Singleton.Height))); session.ExpireTime = TimeProvider.Current.UtcNow.AddMilliseconds(PingCoolingOffPeriod); } } }
public AssetDescriptor(UInt160 asset_id) { using SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); var contract = NativeContract.ContractManagement.GetContract(snapshot, asset_id); if (contract is null) { throw new ArgumentException(); } byte[] script; using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitDynamicCall(asset_id, "decimals", true); sb.EmitDynamicCall(asset_id, "symbol", true); script = sb.ToArray(); } using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, gas: 0_10000000); if (engine.State.HasFlag(VMState.FAULT)) { throw new ArgumentException(); } this.AssetId = asset_id; this.AssetName = contract.Manifest.Name; this.Symbol = engine.ResultStack.Pop().GetString(); this.Decimals = (byte)engine.ResultStack.Pop().GetInteger(); }
public JObject GetNep17Balances(JArray _params) { using SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); UInt160 userScriptHash = GetScriptHashFromParam(_params[0].AsString()); JObject json = new JObject(); JArray balances = new JArray(); json["balance"] = balances; json["address"] = userScriptHash.ToAddress(); var dbCache = new DbCache <Nep17BalanceKey, Nep17Balance>(_db, null, null, Nep17BalancePrefix); byte[] prefix = userScriptHash.ToArray(); foreach (var(key, value) in dbCache.Find(prefix)) { JObject balance = new JObject(); if (NativeContract.ContractManagement.GetContract(snapshot, key.AssetScriptHash) is null) { continue; } balance["assethash"] = key.AssetScriptHash.ToString(); balance["amount"] = value.Balance.ToString(); balance["lastupdatedblock"] = value.LastUpdatedBlock; balances.Add(balance); } return(json); }
protected virtual JObject GetNextBlockValidators(JArray _params) { using SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); var validators = NativeContract.NEO.GetNextBlockValidators(snapshot); var candidates = NativeContract.NEO.GetCandidates(snapshot); if (candidates.Length > 0) { return(candidates.Select(p => { JObject validator = new JObject(); validator["publickey"] = p.PublicKey.ToString(); validator["votes"] = p.Votes.ToString(); validator["active"] = validators.Contains(p.PublicKey); return validator; }).ToArray()); } else { return(validators.Select(p => { JObject validator = new JObject(); validator["publickey"] = p.ToString(); validator["votes"] = 0; validator["active"] = true; return validator; }).ToArray()); } }
public void BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered() { long txFee = 1; AddTransactionsWithBalanceVerify(70, txFee); _unit.SortedTxCount.Should().Be(70); var block = new Block { Transactions = _unit.GetSortedVerifiedTransactions().Take(10).ToArray() }; // Simulate the transfer process in tx by burning the balance UInt160 sender = block.Transactions[0].Sender; SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, sender); ApplicationEngine applicationEngine = ApplicationEngine.Create(TriggerType.All, block, snapshot, (long)balance); NativeContract.GAS.Burn(applicationEngine, sender, balance); NativeContract.GAS.Mint(applicationEngine, sender, txFee * 30); // Set the balance to meet 30 txs only // Persist block and reverify all the txs in mempool, but half of the txs will be discarded _unit.UpdatePoolForBlockPersisted(block, snapshot); _unit.SortedTxCount.Should().Be(30); _unit.UnverifiedSortedTxCount.Should().Be(0); // Revert the balance NativeContract.GAS.Burn(applicationEngine, sender, txFee * 30); NativeContract.GAS.Mint(applicationEngine, sender, balance); }
public static ApplicationEngine Run(byte[] script, IVerifiable container = null, Block persistingBlock = null, bool testMode = false, long extraGAS = default) { using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) { return(Run(script, snapshot, container, persistingBlock, testMode, extraGAS)); } }
private void SaveHeaderHashList(SnapshotView snapshot = null) { if ((header_index.Count - stored_header_count < 2000)) { return; } bool snapshot_created = snapshot == null; if (snapshot_created) { snapshot = GetSnapshot(); } try { while (header_index.Count - stored_header_count >= 2000) { snapshot.HeaderHashList.Add(stored_header_count, new HeaderHashList { Hashes = header_index.Skip((int)stored_header_count).Take(2000).ToArray() }); stored_header_count += 2000; } if (snapshot_created) { snapshot.Commit(); } } finally { if (snapshot_created) { snapshot.Dispose(); } } }
protected virtual JObject GetContractState(JArray _params) { using SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); UInt160 script_hash = ToScriptHash(_params[0].AsString()); ContractState contract = NativeContract.ContractManagement.GetContract(snapshot, script_hash); return(contract?.ToJson() ?? throw new RpcException(-100, "Unknown contract")); }
private void OnInvokeCommand(UInt160 scriptHash, string operation, JArray contractParameters = null, UInt160[] witnessAddress = null) { List <Cosigner> signCollection = new List <Cosigner>(); if (witnessAddress != null && !NoWallet()) { using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) { UInt160[] accounts = CurrentWallet.GetAccounts().Where(p => !p.Lock && !p.WatchOnly).Select(p => p.ScriptHash).Where(p => NativeContract.GAS.BalanceOf(snapshot, p).Sign > 0).ToArray(); foreach (var signAccount in accounts) { if (witnessAddress is null) { break; } foreach (var witness in witnessAddress) { if (witness.Equals(signAccount)) { signCollection.Add(new Cosigner() { Account = signAccount }); break; } } } } } Transaction tx = new Transaction { Sender = UInt160.Zero, Attributes = signCollection.ToArray(), Witnesses = Array.Empty <Witness>(), }; _ = OnInvokeWithResult(scriptHash, operation, tx, contractParameters); if (NoWallet()) { return; } try { tx = CurrentWallet.MakeTransaction(tx.Script, null, tx.Attributes); } catch (InvalidOperationException) { Console.WriteLine("Error: insufficient balance."); return; } if (!ReadUserInput("Relay tx(no|yes)").IsYes()) { return; } SignAndSendTx(tx); }
/// <summary> /// Event fired when the Select None button is clicked /// </summary> private void SelectNoneBtn_Click(object sender, EventArgs e) { foreach (ListViewItem I in SnapshotView.Items) { I.Checked = false; } SnapshotView.Update(); }
public void Execute(Arguments arguments) { GetSnapshotRequest request = CreateRequest(arguments); Snapshot snapshot = mediator.Send(request).Result; SnapshotView snapshotView = new SnapshotView(snapshot); snapshotView.Display(); }
/// <summary> /// Make and send transaction with script, sender /// </summary> /// <param name="script">script</param> /// <param name="account">sender</param> private void SendTransaction(byte[] script, UInt160 account = null) { List <Cosigner> signCollection = new List <Cosigner>(); if (account != null) { using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) { UInt160[] accounts = CurrentWallet.GetAccounts().Where(p => !p.Lock && !p.WatchOnly).Select(p => p.ScriptHash).Where(p => NativeContract.GAS.BalanceOf(snapshot, p).Sign > 0).ToArray(); foreach (var signAccount in accounts) { if (account.Equals(signAccount)) { signCollection.Add(new Cosigner() { Account = signAccount }); break; } } } } try { Transaction tx = CurrentWallet.MakeTransaction(script, account, signCollection?.ToArray()); Console.WriteLine($"Invoking script with: '{tx.Script.ToHexString()}'"); using (ApplicationEngine engine = ApplicationEngine.Run(tx.Script, tx, null, testMode: true)) { Console.WriteLine($"VM State: {engine.State}"); Console.WriteLine($"Gas Consumed: {new BigDecimal(engine.GasConsumed, NativeContract.GAS.Decimals)}"); Console.WriteLine($"Evaluation Stack: {new JArray(engine.ResultStack.Select(p => p.ToParameter().ToJson()))}"); Console.WriteLine(); if (engine.State.HasFlag(VMState.FAULT)) { Console.WriteLine("Engine faulted."); return; } } if (!ReadUserInput("relay tx(no|yes)").IsYes()) { return; } SignAndSendTx(tx); } catch (InvalidOperationException) { Console.WriteLine("Error: insufficient balance."); return; } return; }
private void AddTransactionsWithBalanceVerify(int count, long fee, SnapshotView snapshot) { for (int i = 0; i < count; i++) { var txToAdd = CreateTransactionWithFeeAndBalanceVerify(fee); _unit.TryAdd(txToAdd, snapshot); } Console.WriteLine($"created {count} tx"); }
public IActionResult GetUnclaimedGas() { CheckWallet(); BigInteger gas = BigInteger.Zero; using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) foreach (UInt160 account in wallet.GetAccounts().Select(p => p.ScriptHash)) { gas += NativeContract.NEO.UnclaimedGas(snapshot, account, snapshot.Height + 1); } return(FormatJson(gas.ToString())); }
private JObject GetWalletUnclaimedGas(JArray _params) { CheckWallet(); BigInteger gas = BigInteger.Zero; using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) foreach (UInt160 account in wallet.GetAccounts().Select(p => p.ScriptHash)) { gas += NativeContract.NEO.UnclaimedGas(snapshot, account, snapshot.Height + 1); } return(gas.ToString()); }
/// <summary> /// Sets the data context for snapshot view. /// </summary> /// <returns> Snapshot view </returns> public SnapshotView GetSnapshotView() { if (snapshotViewModel == null) { snapshotViewModel = new SnapshotViewModel(GetSnapshotModel()); snapshotView = new SnapshotView() { DataContext = snapshotViewModel }; } return(snapshotView); }
/// <summary> /// Make and send transaction with script, sender /// </summary> /// <param name="script">script</param> /// <param name="account">sender</param> private void SendTransaction(byte[] script, UInt160 account = null) { Signer[] signers = System.Array.Empty <Signer>(); if (account != null) { using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) { signers = CurrentWallet.GetAccounts() .Where(p => !p.Lock && !p.WatchOnly && p.ScriptHash == account && NativeContract.GAS.BalanceOf(snapshot, p.ScriptHash).Sign > 0) .Select(p => new Signer() { Account = p.ScriptHash, Scopes = WitnessScope.CalledByEntry }) .ToArray(); } } try { Transaction tx = CurrentWallet.MakeTransaction(script, account, signers); Console.WriteLine($"Invoking script with: '{tx.Script.ToHexString()}'"); using (ApplicationEngine engine = ApplicationEngine.Run(tx.Script, tx, null, testMode: true)) { Console.WriteLine($"VM State: {engine.State}"); Console.WriteLine($"Gas Consumed: {new BigDecimal(engine.GasConsumed, NativeContract.GAS.Decimals)}"); Console.WriteLine($"Evaluation Stack: {new JArray(engine.ResultStack.Select(p => p.ToJson()))}"); Console.WriteLine(); if (engine.State.HasFlag(VMState.FAULT)) { Console.WriteLine("Error: " + GetExceptionMessage(engine.FaultException)); return; } } if (!ReadUserInput("relay tx(no|yes)").IsYes()) { return; } SignAndSendTx(tx); } catch (InvalidOperationException e) { Console.WriteLine("Error: " + GetExceptionMessage(e)); return; } return; }
private JObject GetValidators(JArray _params) { using SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); var validators = NativeContract.NEO.GetValidators(snapshot); return(NativeContract.NEO.GetRegisteredValidators(snapshot).Select(p => { JObject validator = new JObject(); validator["publickey"] = p.PublicKey.ToString(); validator["votes"] = p.Votes.ToString(); validator["active"] = validators.Contains(p.PublicKey); return validator; }).ToArray()); }
private void OnShowGasCommand() { if (NoWallet()) { return; } BigInteger gas = BigInteger.Zero; using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) foreach (UInt160 account in CurrentWallet.GetAccounts().Select(p => p.ScriptHash)) { gas += NativeContract.NEO.UnclaimedGas(snapshot, account, snapshot.Height + 1); } Console.WriteLine($"Unclaimed gas: {new BigDecimal(gas, NativeContract.GAS.Decimals)}"); }
/// <summary> /// Make and send transaction with script, sender /// </summary> /// <param name="script">script</param> /// <param name="account">sender</param> private void SendTransaction(byte[] script, UInt160 account = null) { Signer[] signers = System.Array.Empty <Signer>(); if (account != null) { using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) { signers = CurrentWallet.GetAccounts() .Where(p => !p.Lock && !p.WatchOnly && p.ScriptHash == account && NativeContract.GAS.BalanceOf(snapshot, p.ScriptHash).Sign > 0) .Select(p => new Signer() { Account = p.ScriptHash, Scopes = WitnessScope.CalledByEntry }) .ToArray(); } } try { Transaction tx = CurrentWallet.MakeTransaction(script, account, signers); Console.WriteLine($"Invoking script with: '{tx.Script.ToBase64String()}'"); using (ApplicationEngine engine = ApplicationEngine.Run(tx.Script, container: tx)) { PrintExecutionOutput(engine, true); if (engine.State == VMState.FAULT) { return; } } if (!ReadUserInput("Relay tx(no|yes)").IsYes()) { return; } SignAndSendTx(tx); } catch (InvalidOperationException e) { Console.WriteLine("Error: " + GetExceptionMessage(e)); return; } return; }
public IActionResult GetValidators() { JArray json = new JArray(); using SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); var validators = NativeContract.NEO.GetValidators(snapshot); json = NativeContract.NEO.GetRegisteredValidators(snapshot).Select(p => { JObject validator = new JObject(); validator["publickey"] = p.PublicKey.ToString(); validator["votes"] = p.Votes.ToString(); validator["active"] = validators.Contains(p.PublicKey); return(validator); }).ToArray(); return(FormatJson(json)); }
/// <summary> /// show unclaimed gas amount /// </summary> /// <returns></returns> public async Task <object> ShowGas() { if (CurrentWallet == null) { return(Error(ErrorCode.WalletNotOpen)); } BigInteger gas = BigInteger.Zero; using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) foreach (UInt160 account in CurrentWallet.GetAccounts().Select(p => p.ScriptHash)) { gas += NativeContract.NEO.UnclaimedGas(snapshot, account, snapshot.Height + 1); } return(new UnclaimedGasModel() { UnclaimedGas = new BigDecimal(gas, NativeContract.GAS.Decimals) }); }
/// <summary> /// /// </summary> /// <param name="account"></param> /// <param name="snapshot"></param> /// <returns></returns> public static AccountType GetAccountType(this WalletAccount account, SnapshotView snapshot) { if (account.Contract != null) { if (account.Contract.Script.IsMultiSigContract(out _, out int _)) { return(AccountType.MultiSignature); } if (account.Contract.Script.IsSignatureContract()) { return(AccountType.Standard); } if (snapshot.Contracts.TryGet(account.Contract.ScriptHash) != null) { return(AccountType.DeployedContract); } } return(AccountType.NonStandard); }
protected virtual JObject GetStorage(JArray _params) { if (!int.TryParse(_params[0].AsString(), out int id)) { using SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); UInt160 script_hash = UInt160.Parse(_params[0].AsString()); ContractState contract = NativeContract.ContractManagement.GetContract(snapshot, script_hash); if (contract == null) { return(null); } id = contract.Id; } byte[] key = _params[1].AsString().HexToBytes(); StorageItem item = Blockchain.Singleton.View.Storages.TryGet(new StorageKey { Id = id, Key = key }) ?? new StorageItem(); return(item.Value?.ToHexString()); }
private JObject GetUnclaimedGas(JArray _params) { string address = _params[0].AsString(); JObject json = new JObject(); UInt160 script_hash; try { script_hash = address.ToScriptHash(); } catch { script_hash = null; } if (script_hash == null) { throw new RpcException(-100, "Invalid address"); } SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); json["unclaimed"] = NativeContract.NEO.UnclaimedGas(snapshot, script_hash, snapshot.Height + 1).ToString(); json["address"] = script_hash.ToAddress(); return(json); }
private void Persist(Block block) { using (SnapshotView snapshot = GetSnapshot()) { if (block.Index == header_index.Count) { header_index.Add(block.Hash); snapshot.HeaderHashIndex.GetAndChange().Set(block); } List <ApplicationExecuted> all_application_executed = new List <ApplicationExecuted>(); snapshot.PersistingBlock = block; if (block.Index > 0) { using (ApplicationEngine engine = new ApplicationEngine(TriggerType.System, null, snapshot, 0, true)) { engine.LoadScript(onPersistNativeContractScript); if (engine.Execute() != VMState.HALT) { throw new InvalidOperationException(); } ApplicationExecuted application_executed = new ApplicationExecuted(engine); Context.System.EventStream.Publish(application_executed); all_application_executed.Add(application_executed); } } snapshot.Blocks.Add(block.Hash, block.Trim()); StoreView clonedSnapshot = snapshot.Clone(); // Warning: Do not write into variable snapshot directly. Write into variable clonedSnapshot and commit instead. foreach (Transaction tx in block.Transactions) { var state = new TransactionState { BlockIndex = block.Index, Transaction = tx }; clonedSnapshot.Transactions.Add(tx.Hash, state); clonedSnapshot.Transactions.Commit(); using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx, clonedSnapshot, tx.SystemFee)) { engine.LoadScript(tx.Script); state.VMState = engine.Execute(); if (state.VMState == VMState.HALT) { clonedSnapshot.Commit(); } else { clonedSnapshot = snapshot.Clone(); } ApplicationExecuted application_executed = new ApplicationExecuted(engine); Context.System.EventStream.Publish(application_executed); all_application_executed.Add(application_executed); } } snapshot.BlockHashIndex.GetAndChange().Set(block); foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins) { plugin.OnPersist(snapshot, all_application_executed); } snapshot.Commit(); List <Exception> commitExceptions = null; foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins) { try { plugin.OnCommit(snapshot); } catch (Exception ex) { if (plugin.ShouldThrowExceptionFromCommit(ex)) { if (commitExceptions == null) { commitExceptions = new List <Exception>(); } commitExceptions.Add(ex); } } } if (commitExceptions != null) { throw new AggregateException(commitExceptions); } } UpdateCurrentSnapshot(); block_cache.Remove(block.PrevHash); MemPool.UpdatePoolForBlockPersisted(block, currentSnapshot); Context.System.EventStream.Publish(new PersistCompleted { Block = block }); }
protected virtual JObject GetCommittee(JArray _params) { using SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); return(new JArray(NativeContract.NEO.GetCommittee(snapshot).Select(p => (JObject)p.ToString()))); }
private void OnInvokeCommand(UInt160 scriptHash, string operation, JArray contractParameters = null, UInt160[] witnessAddress = null) { List <ContractParameter> parameters = new List <ContractParameter>(); List <Cosigner> signCollection = new List <Cosigner>(); if (!NoWallet() && witnessAddress != null) { using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) { UInt160[] accounts = CurrentWallet.GetAccounts().Where(p => !p.Lock && !p.WatchOnly).Select(p => p.ScriptHash).Where(p => NativeContract.GAS.BalanceOf(snapshot, p).Sign > 0).ToArray(); foreach (var signAccount in accounts) { if (witnessAddress is null) { break; } foreach (var witness in witnessAddress) { if (witness.Equals(signAccount)) { signCollection.Add(new Cosigner() { Account = signAccount }); break; } } } } } if (contractParameters != null) { foreach (var contractParameter in contractParameters) { parameters.Add(ContractParameter.FromJson(contractParameter)); } } Transaction tx = new Transaction { Sender = UInt160.Zero, Attributes = Array.Empty <TransactionAttribute>(), Witnesses = Array.Empty <Witness>(), Cosigners = signCollection.ToArray() }; using (ScriptBuilder scriptBuilder = new ScriptBuilder()) { scriptBuilder.EmitAppCall(scriptHash, operation, parameters.ToArray()); tx.Script = scriptBuilder.ToArray(); Console.WriteLine($"Invoking script with: '{tx.Script.ToHexString()}'"); } using (ApplicationEngine engine = ApplicationEngine.Run(tx.Script, tx, testMode: true)) { Console.WriteLine($"VM State: {engine.State}"); Console.WriteLine($"Gas Consumed: {new BigDecimal(engine.GasConsumed, NativeContract.GAS.Decimals)}"); Console.WriteLine($"Evaluation Stack: {new JArray(engine.ResultStack.Select(p => p.ToParameter().ToJson()))}"); Console.WriteLine(); if (engine.State.HasFlag(VMState.FAULT)) { Console.WriteLine("Engine faulted."); return; } } if (NoWallet()) { return; } try { tx = CurrentWallet.MakeTransaction(tx.Script, null, tx.Attributes, tx.Cosigners); } catch (InvalidOperationException) { Console.WriteLine("Error: insufficient balance."); return; } if (!ReadUserInput("relay tx(no|yes)").IsYes()) { return; } SignAndSendTx(tx); }
private VerifyResult OnNewBlock(Block block) { if (block.Index <= Height) { return(VerifyResult.AlreadyExists); } if (block_cache.ContainsKey(block.Hash)) { return(VerifyResult.AlreadyExists); } if (block.Index - 1 >= header_index.Count) { AddUnverifiedBlockToCache(block); return(VerifyResult.UnableToVerify); } if (block.Index == header_index.Count) { if (!block.Verify(currentSnapshot)) { return(VerifyResult.Invalid); } } else { if (!block.Hash.Equals(header_index[(int)block.Index])) { return(VerifyResult.Invalid); } } if (block.Index == Height + 1) { Block block_persist = block; List <Block> blocksToPersistList = new List <Block>(); while (true) { blocksToPersistList.Add(block_persist); if (block_persist.Index + 1 >= header_index.Count) { break; } UInt256 hash = header_index[(int)block_persist.Index + 1]; if (!block_cache.TryGetValue(hash, out block_persist)) { break; } } int blocksPersisted = 0; foreach (Block blockToPersist in blocksToPersistList) { block_cache_unverified.Remove(blockToPersist.Index); Persist(blockToPersist); // 15000 is the default among of seconds per block, while MilliSecondsPerBlock is the current uint extraBlocks = (15000 - MillisecondsPerBlock) / 1000; if (blocksPersisted++ < blocksToPersistList.Count - (2 + Math.Max(0, extraBlocks))) { continue; } // Empirically calibrated for relaying the most recent 2 blocks persisted with 15s network // Increase in the rate of 1 block per second in configurations with faster blocks if (blockToPersist.Index + 100 >= header_index.Count) { system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = blockToPersist }); } } SaveHeaderHashList(); if (block_cache_unverified.TryGetValue(Height + 1, out LinkedList <Block> unverifiedBlocks)) { foreach (var unverifiedBlock in unverifiedBlocks) { Self.Tell(unverifiedBlock, ActorRefs.NoSender); } block_cache_unverified.Remove(Height + 1); } } else { block_cache.Add(block.Hash, block); if (block.Index + 100 >= header_index.Count) { system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = block }); } if (block.Index == header_index.Count) { header_index.Add(block.Hash); using (SnapshotView snapshot = GetSnapshot()) { snapshot.Blocks.Add(block.Hash, block.Header.Trim()); snapshot.HeaderHashIndex.GetAndChange().Set(block); SaveHeaderHashList(snapshot); snapshot.Commit(); } UpdateCurrentSnapshot(); } } return(VerifyResult.Succeed); }