public override void Put(byte prefix, byte[] key, byte[] value) { db.Put(WriteOptions.Default, SliceBuilder.Begin(prefix).Add(key), value); }
public override void PutSync(byte prefix, byte[] key, byte[] value) { db.Put(new WriteOptions { Sync = true }, SliceBuilder.Begin(prefix).Add(key), value); }
static WalletIndexer() { string path = Path.GetFullPath($"Index_{Settings.Default.Magic:X8}"); Directory.CreateDirectory(path); db = DB.Open(path, new Options { CreateIfMissing = true }); if (db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.SYS_Version), out Slice value) && Version.TryParse(value.ToString(), out Version version) && version >= Version.Parse("2.5.4")) { ReadOptions options = new ReadOptions { FillCache = false }; foreach (var group in db.Find(options, SliceBuilder.Begin(DataEntryPrefix.IX_Group), (k, v) => new { Height = k.ToUInt32(1), Id = v.ToArray() })) { UInt160[] accounts = db.Get(options, SliceBuilder.Begin(DataEntryPrefix.IX_Accounts).Add(group.Id)).ToArray().AsSerializableArray <UInt160>(); indexes.Add(group.Height, new HashSet <UInt160>(accounts)); foreach (UInt160 account in accounts) { accounts_tracked.Add(account, new HashSet <CoinReference>()); } } foreach (Coin coin in db.Find(options, SliceBuilder.Begin(DataEntryPrefix.ST_Coin), (k, v) => new Coin { Reference = k.ToArray().Skip(1).ToArray().AsSerializable <CoinReference>(), Output = v.ToArray().AsSerializable <TransactionOutput>(), State = (CoinState)v.ToArray()[60] })) { accounts_tracked[coin.Output.ScriptHash].Add(coin.Reference); coins_tracked.Add(coin.Reference, coin); } } else { WriteBatch batch = new WriteBatch(); ReadOptions options = new ReadOptions { FillCache = false }; using (Iterator it = db.NewIterator(options)) { for (it.SeekToFirst(); it.Valid(); it.Next()) { batch.Delete(it.Key()); } } batch.Put(SliceBuilder.Begin(DataEntryPrefix.SYS_Version), Assembly.GetExecutingAssembly().GetName().Version.ToString()); db.Write(WriteOptions.Default, batch); } Thread thread = new Thread(ProcessBlocks) { IsBackground = true, Name = $"{nameof(WalletIndexer)}.{nameof(ProcessBlocks)}" }; thread.Start(); }
private void ProcessBlock(Block block, HashSet <UInt160> accounts, WriteBatch batch) { foreach (Transaction tx in block.Transactions) { HashSet <UInt160> accounts_changed = new HashSet <UInt160>(); for (ushort index = 0; index < tx.Outputs.Length; index++) { TransactionOutput output = tx.Outputs[index]; if (accounts_tracked.ContainsKey(output.ScriptHash)) { CoinReference reference = new CoinReference { PrevHash = tx.Hash, PrevIndex = index }; if (coins_tracked.TryGetValue(reference, out Coin coin)) { coin.State |= CoinState.Confirmed; } else { accounts_tracked[output.ScriptHash].Add(reference); coins_tracked.Add(reference, coin = new Coin { Reference = reference, Output = output, State = CoinState.Confirmed }); } batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_Coin).Add(reference), SliceBuilder.Begin().Add(output).Add((byte)coin.State)); accounts_changed.Add(output.ScriptHash); } } foreach (CoinReference input in tx.Inputs) { if (coins_tracked.TryGetValue(input, out Coin coin)) { if (coin.Output.AssetId.Equals(Blockchain.GoverningToken.Hash)) { coin.State |= CoinState.Spent | CoinState.Confirmed; batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_Coin).Add(input), SliceBuilder.Begin().Add(coin.Output).Add((byte)coin.State)); } else { accounts_tracked[coin.Output.ScriptHash].Remove(input); coins_tracked.Remove(input); batch.Delete(DataEntryPrefix.ST_Coin, input); } accounts_changed.Add(coin.Output.ScriptHash); } } if (tx is ClaimTransaction ctx) { foreach (CoinReference claim in ctx.Claims) { if (coins_tracked.TryGetValue(claim, out Coin coin)) { accounts_tracked[coin.Output.ScriptHash].Remove(claim); coins_tracked.Remove(claim); batch.Delete(DataEntryPrefix.ST_Coin, claim); accounts_changed.Add(coin.Output.ScriptHash); } } } if (accounts_changed.Count > 0) { foreach (UInt160 account in accounts_changed) { batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_Transaction).Add(account).Add(tx.Hash), false); } BalanceChanged?.Invoke(null, new BalanceEventArgs { Transaction = tx, RelatedAccounts = accounts_changed.ToArray(), Height = block.Index, Time = block.Timestamp }); } } }
public static T Get <T>(this Database db, ReadOptions options, byte prefix, ISerializable key, Func <Slice, T> resultSelector) { return(resultSelector(db.Get(options, SliceBuilder.Begin(prefix).Add(key)))); }
public static void Put(this WriteBatch batch, byte prefix, ISerializable key, ISerializable value) { batch.Put(SliceBuilder.Begin(prefix).Add(key), value.ToArray()); }
public static IEnumerable <T> Find <T>(this Database db, ReadOptions options, byte prefix) where T : class, ISerializable, new() { return(Find(db, options, SliceBuilder.Begin(prefix), (k, v) => v.ToArray().AsSerializable <T>())); }
public static T Get <T>(this Database db, ReadOptions options, byte prefix, ISerializable key) where T : class, ISerializable, new() { return(db.Get(options, SliceBuilder.Begin(prefix).Add(key)).ToArray().AsSerializable <T>()); }
protected override IEnumerable <KeyValuePair <TKey, TValue> > FindInternal(byte[] key_prefix) { return(db.Find(options, SliceBuilder.Begin(prefix).Add(key_prefix), (k, v) => new KeyValuePair <TKey, TValue>(k.ToArray().AsSerializable <TKey>(1), v.ToArray().AsSerializable <TValue>()))); }
public static void Delete(this WriteBatch batch, byte prefix, ISerializable key) { batch.Delete(SliceBuilder.Begin(prefix).Add(key)); }
private void Persist(Block block) { WriteBatch batch = new WriteBatch(); DbCache <UInt160, AccountState> accounts = new DbCache <UInt160, AccountState>(db, DataEntryPrefix.ST_Account, batch); DbCache <UInt256, UnspentCoinState> unspentcoins = new DbCache <UInt256, UnspentCoinState>(db, DataEntryPrefix.ST_Coin, batch); DbCache <UInt256, SpentCoinState> spentcoins = new DbCache <UInt256, SpentCoinState>(db, DataEntryPrefix.ST_SpentCoin, batch); DbCache <ECPoint, ValidatorState> validators = new DbCache <ECPoint, ValidatorState>(db, DataEntryPrefix.ST_Validator, batch); DbCache <UInt256, AssetState> assets = new DbCache <UInt256, AssetState>(db, DataEntryPrefix.ST_Asset, batch); DbCache <UInt160, ContractState> contracts = new DbCache <UInt160, ContractState>(db, DataEntryPrefix.ST_Contract, batch); DbCache <StorageKey, StorageItem> storages = new DbCache <StorageKey, StorageItem>(db, DataEntryPrefix.ST_Storage, batch); DbMetaDataCache <ValidatorsCountState> validators_count = new DbMetaDataCache <ValidatorsCountState>(db, DataEntryPrefix.IX_ValidatorsCount); CachedScriptTable script_table = new CachedScriptTable(contracts); 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(output.ScriptHash)); if (account.Balances.ContainsKey(output.AssetId)) { account.Balances[output.AssetId] += output.Value; } else { account.Balances[output.AssetId] = output.Value; } if (output.AssetId.Equals(GoverningToken.Hash) && account.Votes.Length > 0) { foreach (ECPoint pubkey in account.Votes) { validators.GetAndChange(pubkey, () => new ValidatorState(pubkey)).Votes += output.Value; } validators_count.GetAndChange().Votes[account.Votes.Length - 1] += output.Value; } } foreach (var group in tx.Inputs.GroupBy(p => p.PrevHash)) { Transaction tx_prev = GetTransaction(ReadOptions.Default, group.Key, out int height); foreach (CoinReference input in group) { unspentcoins.GetAndChange(input.PrevHash).Items[input.PrevIndex] |= CoinState.Spent; TransactionOutput out_prev = tx_prev.Outputs[input.PrevIndex]; AccountState account = accounts.GetAndChange(out_prev.ScriptHash); if (out_prev.AssetId.Equals(GoverningToken.Hash)) { spentcoins.GetAndChange(input.PrevHash, () => new SpentCoinState { TransactionHash = input.PrevHash, TransactionHeight = (uint)height, Items = new Dictionary <ushort, uint>() }).Items.Add(input.PrevIndex, block.Index); if (account.Votes.Length > 0) { foreach (ECPoint pubkey in account.Votes) { ValidatorState validator = validators.GetAndChange(pubkey); validator.Votes -= out_prev.Value; if (!validator.Registered && validator.Votes.Equals(Fixed8.Zero)) { validators.Delete(pubkey); } } validators_count.GetAndChange().Votes[account.Votes.Length - 1] -= out_prev.Value; } } account.Balances[out_prev.AssetId] -= out_prev.Value; } } switch (tx) { #pragma warning disable CS0612 case RegisterTransaction tx_register: assets.Add(tx.Hash, new AssetState { AssetId = tx_register.Hash, AssetType = tx_register.AssetType, Name = tx_register.Name, Amount = tx_register.Amount, Available = Fixed8.Zero, Precision = tx_register.Precision, Fee = Fixed8.Zero, FeeAddress = new UInt160(), Owner = tx_register.Owner, Admin = tx_register.Admin, Issuer = tx_register.Admin, Expiration = block.Index + 2 * 2000000, IsFrozen = false }); break; #pragma warning restore CS0612 case IssueTransaction _: foreach (TransactionResult result in tx.GetTransactionResults().Where(p => p.Amount < Fixed8.Zero)) { assets.GetAndChange(result.AssetId).Available -= result.Amount; } break; case ClaimTransaction _: foreach (CoinReference input in ((ClaimTransaction)tx).Claims) { if (spentcoins.TryGet(input.PrevHash)?.Items.Remove(input.PrevIndex) == true) { spentcoins.GetAndChange(input.PrevHash); } } break; #pragma warning disable CS0612 case EnrollmentTransaction tx_enrollment: validators.GetAndChange(tx_enrollment.PublicKey, () => new ValidatorState(tx_enrollment.PublicKey)).Registered = true; break; #pragma warning restore CS0612 case StateTransaction tx_state: foreach (StateDescriptor descriptor in tx_state.Descriptors) { switch (descriptor.Type) { case StateType.Account: ProcessAccountStateDescriptor(descriptor, accounts, validators, validators_count); break; case StateType.Validator: ProcessValidatorStateDescriptor(descriptor, validators); break; } } break; #pragma warning disable CS0612 case PublishTransaction tx_publish: contracts.GetOrAdd(tx_publish.ScriptHash, () => new ContractState { Script = tx_publish.Script, ParameterList = tx_publish.ParameterList, ReturnType = tx_publish.ReturnType, ContractProperties = (ContractPropertyState)Convert.ToByte(tx_publish.NeedStorage), Name = tx_publish.Name, CodeVersion = tx_publish.CodeVersion, Author = tx_publish.Author, Email = tx_publish.Email, Description = tx_publish.Description }); break; #pragma warning restore CS0612 case InvocationTransaction tx_invocation: using (StateMachine service = new StateMachine(block, accounts, assets, contracts, storages)) { ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx_invocation, script_table, service, tx_invocation.Gas); engine.LoadScript(tx_invocation.Script, false); if (engine.Execute()) { service.Commit(); } ApplicationExecuted?.Invoke(this, new ApplicationExecutedEventArgs(tx_invocation, service.Notifications.ToArray(), engine)); } break; } foreach (UInt160 hash in tx.Outputs.Select(p => p.ScriptHash).Distinct()) { ContractState contract = contracts.TryGet(hash); if (contract == null) { continue; } using (StateMachine service = new StateMachine(block, accounts, assets, contracts, storages)) { ApplicationEngine engine = new ApplicationEngine(TriggerType.ApplicationR, tx, script_table, service, Fixed8.Zero); engine.LoadScript(contract.Script, false); using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitPush(0); sb.Emit(OpCode.PACK); sb.EmitPush("received"); engine.LoadScript(sb.ToArray(), false); } if (engine.Execute()) { service.Commit(); } } } } accounts.DeleteWhere((k, v) => !v.IsFrozen && v.Votes.Length == 0 && v.Balances.All(p => p.Value <= Fixed8.Zero)); accounts.Commit(); unspentcoins.DeleteWhere((k, v) => v.Items.All(p => p.HasFlag(CoinState.Spent))); unspentcoins.Commit(); spentcoins.DeleteWhere((k, v) => v.Items.Count == 0); spentcoins.Commit(); validators.Commit(); assets.Commit(); contracts.Commit(); storages.Commit(); validators_count.Commit(batch); 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; }
private void OnAddHeader(Header header, WriteBatch batch) { header_index.Add(header.Hash); while ((int)header.Index - 2000 >= stored_header_count) { using (MemoryStream ms = new MemoryStream()) using (BinaryWriter w = new BinaryWriter(ms)) { w.Write(header_index.Skip((int)stored_header_count).Take(2000).ToArray()); w.Flush(); batch.Put(SliceBuilder.Begin(DataEntryPrefix.IX_HeaderHashList).Add(stored_header_count), ms.ToArray()); } stored_header_count += 2000; } batch.Put(SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(header.Hash), SliceBuilder.Begin().Add(0L).Add(header.ToArray())); batch.Put(SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentHeader), SliceBuilder.Begin().Add(header.Hash).Add(header.Index)); }
public LevelDBBlockchain(string path) { header_index.Add(GenesisBlock.Hash); Version version; Slice value; db = DB.Open(path, new Options { CreateIfMissing = true }); if (db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.SYS_Version), out value) && Version.TryParse(value.ToString(), out version) && version >= Version.Parse("2.7.4")) { ReadOptions options = new ReadOptions { FillCache = false }; value = db.Get(options, SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentBlock)); UInt256 current_header_hash = new UInt256(value.ToArray().Take(32).ToArray()); this.current_block_height = value.ToArray().ToUInt32(32); uint current_header_height = current_block_height; if (db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.SYS_CurrentHeader), out value)) { current_header_hash = new UInt256(value.ToArray().Take(32).ToArray()); current_header_height = value.ToArray().ToUInt32(32); } foreach (UInt256 hash in db.Find(options, SliceBuilder.Begin(DataEntryPrefix.IX_HeaderHashList), (k, v) => { using (MemoryStream ms = new MemoryStream(v.ToArray(), false)) using (BinaryReader r = new BinaryReader(ms)) { return(new { Index = k.ToArray().ToUInt32(1), Hashes = r.ReadSerializableArray <UInt256>() }); } }).OrderBy(p => p.Index).SelectMany(p => p.Hashes).ToArray()) { if (!hash.Equals(GenesisBlock.Hash)) { header_index.Add(hash); } stored_header_count++; } if (stored_header_count == 0) { Header[] headers = db.Find(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Block), (k, v) => Header.FromTrimmedData(v.ToArray(), sizeof(long))).OrderBy(p => p.Index).ToArray(); for (int i = 1; i < headers.Length; i++) { header_index.Add(headers[i].Hash); } } else if (current_header_height >= stored_header_count) { for (UInt256 hash = current_header_hash; hash != header_index[(int)stored_header_count - 1];) { Header header = Header.FromTrimmedData(db.Get(options, SliceBuilder.Begin(DataEntryPrefix.DATA_Block).Add(hash)).ToArray(), sizeof(long)); header_index.Insert((int)stored_header_count, hash); hash = header.PrevHash; } } } else { WriteBatch batch = new WriteBatch(); ReadOptions options = new ReadOptions { FillCache = false }; using (Iterator it = db.NewIterator(options)) { for (it.SeekToFirst(); it.Valid(); it.Next()) { batch.Delete(it.Key()); } } db.Write(WriteOptions.Default, batch); Persist(GenesisBlock); db.Put(WriteOptions.Default, SliceBuilder.Begin(DataEntryPrefix.SYS_Version), GetType().GetTypeInfo().Assembly.GetName().Version.ToString()); } thread_persistence = new Thread(PersistBlocks); thread_persistence.Name = "LevelDBBlockchain.PersistBlocks"; thread_persistence.Priority = ThreadPriority.AboveNormal; thread_persistence.Start(); }
public override bool ContainsTransaction(UInt256 hash) { Slice value; return(db.TryGet(ReadOptions.Default, SliceBuilder.Begin(DataEntryPrefix.DATA_Transaction).Add(hash), out value)); }
private void ProcessBlock(Block block, HashSet <UInt160> accounts, WriteBatch batch) { foreach (Transaction tx in block.Transactions) { HashSet <UInt160> accounts_changed = new HashSet <UInt160>(); for (ushort index = 0; index < tx.Outputs.Length; index++) { TransactionOutput output = tx.Outputs[index]; if (accounts_tracked.ContainsKey(output.ScriptHash)) { CoinReference reference = new CoinReference { PrevHash = tx.Hash, PrevIndex = index }; if (coins_tracked.TryGetValue(reference, out Coin coin)) { coin.State |= CoinState.Confirmed; } else { accounts_tracked[output.ScriptHash].Add(reference); coins_tracked.Add(reference, coin = new Coin { Reference = reference, Output = output, State = CoinState.Confirmed }); } batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_Coin).Add(reference), SliceBuilder.Begin().Add(output).Add((byte)coin.State)); accounts_changed.Add(output.ScriptHash); } } foreach (CoinReference input in tx.Inputs) { if (coins_tracked.TryGetValue(input, out Coin coin)) { if (coin.Output.AssetId.Equals(Blockchain.GoverningToken.Hash)) { coin.State |= CoinState.Spent | CoinState.Confirmed; batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_Coin).Add(input), SliceBuilder.Begin().Add(coin.Output).Add((byte)coin.State)); } else { accounts_tracked[coin.Output.ScriptHash].Remove(input); coins_tracked.Remove(input); batch.Delete(DataEntryPrefix.ST_Coin, input); } accounts_changed.Add(coin.Output.ScriptHash); } } switch (tx) { case MinerTransaction _: case ContractTransaction _: #pragma warning disable CS0612 case PublishTransaction _: #pragma warning restore CS0612 break; case ClaimTransaction tx_claim: foreach (CoinReference claim in tx_claim.Claims) { if (coins_tracked.TryGetValue(claim, out Coin coin)) { accounts_tracked[coin.Output.ScriptHash].Remove(claim); coins_tracked.Remove(claim); batch.Delete(DataEntryPrefix.ST_Coin, claim); accounts_changed.Add(coin.Output.ScriptHash); } } break; #pragma warning disable CS0612 case EnrollmentTransaction tx_enrollment: if (accounts_tracked.ContainsKey(tx_enrollment.ScriptHash)) { accounts_changed.Add(tx_enrollment.ScriptHash); } break; case RegisterTransaction tx_register: if (accounts_tracked.ContainsKey(tx_register.OwnerScriptHash)) { accounts_changed.Add(tx_register.OwnerScriptHash); } break; #pragma warning restore CS0612 default: foreach (UInt160 hash in tx.Witnesses.Select(p => p.ScriptHash)) { if (accounts_tracked.ContainsKey(hash)) { accounts_changed.Add(hash); } } break; } if (accounts_changed.Count > 0) { foreach (UInt160 account in accounts_changed) { batch.Put(SliceBuilder.Begin(DataEntryPrefix.ST_Transaction).Add(account).Add(tx.Hash), false); } WalletTransaction?.Invoke(null, new WalletTransactionEventArgs { Transaction = tx, RelatedAccounts = accounts_changed.ToArray(), Height = block.Index, Time = block.Timestamp }); } } }