public CachedScriptTable(DbCache <UInt160, ContractState> contracts) { this.contracts = contracts; }
private void Persist(Block block) { WriteBatch batch = new WriteBatch(); DbCache <UInt160, AccountState> accounts = new DbCache <UInt160, AccountState>(db, DataEntryPrefix.ST_Account); DbCache <UInt256, UnspentCoinState> unspentcoins = new DbCache <UInt256, UnspentCoinState>(db, DataEntryPrefix.ST_Coin); DbCache <UInt256, SpentCoinState> spentcoins = new DbCache <UInt256, SpentCoinState>(db, DataEntryPrefix.ST_SpentCoin); DbCache <ECPoint, ValidatorState> validators = new DbCache <ECPoint, ValidatorState>(db, DataEntryPrefix.ST_Validator); DbCache <UInt256, AssetState> assets = new DbCache <UInt256, AssetState>(db, DataEntryPrefix.ST_Asset); DbCache <UInt160, ContractState> contracts = new DbCache <UInt160, ContractState>(db, DataEntryPrefix.ST_Contract); DbCache <StorageKey, StorageItem> storages = new DbCache <StorageKey, StorageItem>(db, DataEntryPrefix.ST_Storage); long amount_sysfee = GetSysFeeAmount(block.PrevHash) + (long)block.Transactions.Sum(p => p.SystemFee); batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(block.Hash), SliceBuilder.Begin().Add(amount_sysfee).Add(block.Trim())); foreach (Transaction tx in block.Transactions) { batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Transaction).Add(tx.Hash), SliceBuilder.Begin().Add(block.Index).Add(tx.ToArray())); unspentcoins.Add(tx.Hash, new UnspentCoinState { Items = Enumerable.Repeat(CoinState.Confirmed, tx.Outputs.Length).ToArray() }); foreach (TransactionOutput output in tx.Outputs) { AccountState account = accounts.GetAndChange(output.ScriptHash, () => new AccountState { ScriptHash = output.ScriptHash, IsFrozen = false, Votes = new ECPoint[0], Balances = new Dictionary <UInt256, Fixed8>() }); if (account.Balances.ContainsKey(output.AssetId)) { account.Balances[output.AssetId] += output.Value; } else { account.Balances[output.AssetId] = output.Value; } } foreach (var group in tx.Inputs.GroupBy(p => p.PrevHash)) { int height; Transaction tx_prev = GetTransaction(ReadOptions.Default, group.Key, out height); foreach (CoinReference input in group) { unspentcoins.GetAndChange(input.PrevHash).Items[input.PrevIndex] |= CoinState.Spent; if (tx_prev.Outputs[input.PrevIndex].AssetId.Equals(SystemShare.Hash)) { spentcoins.GetAndChange(input.PrevHash, () => new SpentCoinState { TransactionHash = input.PrevHash, TransactionHeight = (uint)height, Items = new Dictionary <ushort, uint>() }).Items.Add(input.PrevIndex, block.Index); } accounts.GetAndChange(tx_prev.Outputs[input.PrevIndex].ScriptHash).Balances[tx_prev.Outputs[input.PrevIndex].AssetId] -= tx_prev.Outputs[input.PrevIndex].Value; } } switch (tx.Type) { case TransactionType.RegisterTransaction: { RegisterTransaction rtx = (RegisterTransaction)tx; assets.Add(tx.Hash, new AssetState { AssetId = rtx.Hash, AssetType = rtx.AssetType, Name = rtx.Name, Amount = rtx.Amount, Available = Fixed8.Zero, Precision = rtx.Precision, Fee = Fixed8.Zero, FeeAddress = new UInt160(), Owner = rtx.Owner, Admin = rtx.Admin, Issuer = rtx.Admin, Expiration = block.Index + 2000000, IsFrozen = false }); } break; case TransactionType.IssueTransaction: foreach (TransactionResult result in tx.GetTransactionResults().Where(p => p.Amount < Fixed8.Zero)) { assets.GetAndChange(result.AssetId).Available -= result.Amount; } break; case TransactionType.ClaimTransaction: foreach (CoinReference input in ((ClaimTransaction)tx).Claims) { if (spentcoins.TryGet(input.PrevHash)?.Items.Remove(input.PrevIndex) == true) { spentcoins.GetAndChange(input.PrevHash); } } break; case TransactionType.EnrollmentTransaction: { EnrollmentTransaction enroll_tx = (EnrollmentTransaction)tx; validators.Add(enroll_tx.PublicKey, new ValidatorState { PublicKey = enroll_tx.PublicKey }); } break; case TransactionType.PublishTransaction: { PublishTransaction publish_tx = (PublishTransaction)tx; contracts.GetOrAdd(publish_tx.Code.ScriptHash, () => new ContractState { Code = publish_tx.Code, HasStorage = publish_tx.NeedStorage, Name = publish_tx.Name, CodeVersion = publish_tx.CodeVersion, Author = publish_tx.Author, Email = publish_tx.Email, Description = publish_tx.Description }); } break; case TransactionType.InvocationTransaction: { InvocationTransaction itx = (InvocationTransaction)tx; CachedScriptTable script_table = new CachedScriptTable(contracts); StateMachine service = new StateMachine(accounts, validators, assets, contracts, storages); ApplicationEngine engine = new ApplicationEngine(itx, script_table, service, itx.Gas); engine.LoadScript(itx.Script, false); if (engine.Execute()) { service.Commit(); } } break; } } accounts.DeleteWhere((k, v) => !v.IsFrozen && v.Votes.Length == 0 && v.Balances.All(p => p.Value <= Fixed8.Zero)); accounts.Commit(batch); unspentcoins.DeleteWhere((k, v) => v.Items.All(p => p.HasFlag(CoinState.Spent))); unspentcoins.Commit(batch); spentcoins.DeleteWhere((k, v) => v.Items.Count == 0); spentcoins.Commit(batch); validators.Commit(batch); assets.Commit(batch); HashSet <UInt160> contracts_deleted = new HashSet <UInt160>(contracts.GetChangeSet().Where(p => p.State == TrackState.Deleted).Select(p => p.Key)); contracts.Commit(batch); if (contracts_deleted.Count > 0) { storages.DeleteWhere((k, v) => contracts_deleted.Contains(k.ScriptHash)); } storages.Commit(batch); foreach (UInt160 script_hash in contracts_deleted) { foreach (Slice key in db.Find(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.ST_Storage).Add(script_hash), (k, v) => k)) { batch.Delete(key); } } batch.Put(SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentBlock), SliceBuilder.Begin().Add(block.Hash).Add(block.Index)); db.Write(WriteOptions.Default, batch); current_block_height = block.Index; }