static void ProcessPoWReward(Network.Models.Transaction tx, Model.BCExplorerContext context) { foreach (var vout in tx.TransactionsOut) { //var vout = tx.TransactionsOut; var address = vout.Address; var amount = vout.Value; Model.Address existing = context.Addresses.Find(address); if (existing == null) { var newAddress = new Model.Address { Id = address, Balance = amount, LastModifiedBlockHeight = (int)tx.Block.Height, }; InsertAddressIfShouldBeIndexed(newAddress, context); UpdateTxIdBlob(newAddress, tx.TransactionId, tx.Time, newAddress.Balance, amount, context); return; } existing.Balance += amount; existing.LastModifiedBlockHeight = (int)tx.Block.Height; UpdateTxIdBlob(existing, tx.TransactionId, tx.Time, existing.Balance, amount, context); } }
static async Task Index(CancellationToken token) { bool wait = false; while (!token.IsCancellationRequested) { if (wait) { _logger.Log(LogLevel.Information, $"Waiting for {_interval} seconds."); Thread.Sleep(TimeSpan.FromSeconds(_interval)); wait = false; } try { using (var context = new Model.BCExplorerContext()) { using (IDbContextTransaction dbtx = context.Database.BeginTransaction(System.Data.IsolationLevel.Serializable)) { if (await context.Blocks.AnyAsync()) { _currentBlockNumber = await context.Blocks.MaxAsync(p => p.Id); _currentBlockHeight = _currentBlockNumber - 1; _currentBlockHeight++; _currentBlockNumber++; } } var tipHash = await _client.GetBestBlockHashAsync(); var tip = await _client.GetBlockAsync(tipHash); if (_currentBlockHeight > tip.Height - 3) { Console.WriteLine($"Block at height {_currentBlockHeight} is not yet mature enough...."); wait = true; continue; } string blockHash = await _client.GetBlockHashAsync(_currentBlockHeight); var result = await IndexBlock(context, blockHash); if (!result) { return; } } } catch (Exception e) { _logger.Log(LogLevel.Error, $"Error creating transaction: {e.Message}"); _logger.Log(LogLevel.Error, $"Suspending for {_suspendInterval} seconds."); Thread.Sleep(TimeSpan.FromSeconds(_suspendInterval)); _logger.Log(LogLevel.Error, $"Retrying..."); } } }
public async Task <Address> GetAddress(string id, int page, int itemsOnPage) { using (var context = new Model.BCExplorerContext()) { var addressFromDb = await context.Addresses.FindAsync(id); if (addressFromDb == null) { return(null); } List <Transaction> transactions = new List <Transaction>(); var addressTransactions = _transactionService.GetTransactionsForAddress(id, page, itemsOnPage); var transactionCount = _transactionService.GetTransactionCountForAddress(id); var totalSent = _transactionService.GetTotalSent(id); var totalReceived = _transactionService.GetTotalReceived(id); var address = new Address() { Id = id, Balance = addressFromDb.Balance, LastModifiedBlockHeight = addressFromDb.LastModifiedBlockHeight, Transactions = addressTransactions, TotalTransactions = transactionCount, TotalSent = totalSent, TotalReceived = totalReceived }; return(address); } }
public async Task <Address> GetAddress(string id) { using (var context = new Model.BCExplorerContext()) { var addressFromDb = await context.Addresses.FindAsync(id); if (addressFromDb == null) { return(null); } List <Transaction> transactions = new List <Transaction>(); var addressTransactions = _transactionService.GetTransactionsForAddress(id); var address = new Address() { Id = id, Balance = addressFromDb.Balance, LastModifiedBlockHeight = addressFromDb.LastModifiedBlockHeight, Transactions = addressTransactions, TotalTransactions = addressTransactions.Count, }; return(address); } }
public async Task <List <Block> > GetLatestBlocks(int count) { List <Block> latestBlocks = new List <Block>(); using (Model.BCExplorerContext context = new Model.BCExplorerContext()) { var blocks = await context.Blocks.OrderByDescending(p => p.Id).Take(count).ToListAsync(); foreach (var block in blocks) { var blockData = JsonConvert.DeserializeObject <RpcResult <BlockResult> >(block.BlockData); var b = new Block { Bits = blockData.Result.Bits, Chainwork = blockData.Result.Chainwork, Confirmations = blockData.Result.Confirmations, Difficulty = blockData.Result.Difficulty, Hash = blockData.Result.Hash, Height = blockData.Result.Height, MerkleRoot = blockData.Result.Merkleroot, NextBlock = blockData.Result.Nextblockhash, Nonce = blockData.Result.Nonce, PreviousBlock = blockData.Result.Previousblockhash, Size = blockData.Result.Size, Time = blockData.Result.GetTime(), Age = blockData.Result.GetAge(), TotalTransactions = blockData.Result.Transactions.Count, Version = (uint)blockData.Result.Version }; latestBlocks.Add(b); } } return(latestBlocks); }
private static async Task <bool> IndexBlock(Model.BCExplorerContext context, string blockHash) { _logger.LogInformation($"Processing block at height {_currentBlockHeight}: {blockHash}"); BlockResult blockResult = await _client.GetBlockAsync(blockHash); if (blockResult == null) { _logger.LogWarning($"Warning - could not retrieve block at height {_currentBlockHeight}: {blockHash}"); return(false); } var block = new Model.Block { Id = _currentBlockNumber, Height = _currentBlockHeight, BlockHash = blockHash, BlockData = blockResult.OriginalJson }; context.Blocks.Add(block); if (_currentBlockHeight == 0) { // for the transaction in the genesis block, we can't pull transaction data, so this is all we know var genesisBlockTransaction = new Model.Transaction { Block = block, Id = blockResult.Transactions[0] }; context.Transactions.Add(genesisBlockTransaction); _currentBlockHeight++; _currentBlockNumber++; context.SaveChanges(); return(true); } List <BCExplorer.Network.Models.Transaction> blockTransactions = new List <BCExplorer.Network.Models.Transaction>(); foreach (var txid in blockResult.Transactions) { var transaction = await _transactionProvider.GetTransaction(txid); transaction.Block = new BCExplorer.Network.Models.Block { Height = _currentBlockHeight }; blockTransactions.Add(transaction); } foreach (var blockTx in blockTransactions) { ProcessTransaction(blockTx, context); } context.SaveChanges(); _currentBlockHeight++; _currentBlockNumber++; return(true); }
static void InsertAddressIfShouldBeIndexed(Model.Address address, Model.BCExplorerContext context) { if (address.Id.StartsWith("OP_RETURN", StringComparison.OrdinalIgnoreCase)) { address.Id = "OP_RETURN"; } if (address.Id != TransactionProvider.NON_STANDARD) { context.Addresses.Add(address); } }
private static void ProcessMoneyTransfer(Network.Models.Transaction tx, Model.BCExplorerContext context) { List <Network.Models.Transaction.TransactionIn> vins = tx.TransactionsIn; List <string> inAdresses = new List <string>(); foreach (var vin in vins) { Model.Address existing = context.Addresses.Find(vin.PrevVOutFetchedAddress); if (existing == null) { _logger.LogWarning($"{vin.PrevVOutFetchedAddress} could not be found."); continue; } inAdresses.Add(existing.Id); existing.Balance -= vin.PrevVOutFetchedValue; existing.LastModifiedBlockHeight = (int)tx.Block.Height; UpdateTxIdBlob(existing, tx.TransactionId, tx.Time, existing.Balance, -vin.PrevVOutFetchedValue, context); } IList <Network.Models.Transaction.TransactionOut> vouts = tx.TransactionsOut; foreach (var vout in vouts) { string outAdress = vout.Address; if (outAdress.StartsWith("OP_RETURN", StringComparison.OrdinalIgnoreCase)) { outAdress = "OP_RETURN"; } Model.Address existing = context.Addresses.Find(outAdress); if (existing != null) { existing.Balance += vout.Value; if (!inAdresses.Contains(existing.Id)) { UpdateTxIdBlob(existing, tx.TransactionId, tx.Time, existing.Balance, vout.Value, context); existing.LastModifiedBlockHeight = (int)tx.Block.Height; } } else { var newAddress = new Model.Address { Id = vout.Address, Balance = vout.Value, LastModifiedBlockHeight = (int)tx.Block.Height }; InsertAddressIfShouldBeIndexed(newAddress, context); UpdateTxIdBlob(newAddress, tx.TransactionId, tx.Time, newAddress.Balance, vout.Value, context); } } }
private static void UpdateTxIdBlob(Model.Address existing, string transactionId, DateTime time, decimal balance, decimal amount, Model.BCExplorerContext context) { var at = new Model.AddressTransaction() { Address = existing, TimeStamp = time, Balance = balance, Amount = amount, TransactionId = transactionId }; context.AddressTransactions.Add(at); }
static void ProcessTransaction(Network.Models.Transaction blockTx, Model.BCExplorerContext context) { switch (blockTx.TransactionType) { case TransactionType.PoW_Reward_Coinbase: ProcessPoWReward(blockTx, context); break; case TransactionType.PoS_Reward: ProcessStakingReward(blockTx, context); break; case TransactionType.Money: ProcessMoneyTransfer(blockTx, context); break; default: throw new IndexOutOfRangeException("Unsupported TransactionType."); } }
static void ProcessStakingReward(Network.Models.Transaction tx, Model.BCExplorerContext context) { foreach (var vin in tx.TransactionsIn) { //var vin = tx.TransactionsIn[0]; var inAddress = vin.PrevVOutFetchedAddress; var oldBalance = vin.PrevVOutFetchedValue; var outValue1 = tx.TransactionsOut[1].Value; var outValue2 = tx.TransactionsOut[2].Value; var change = outValue1 + outValue2 - oldBalance; // we can assume that only pre-existing addresses get a staking reward Model.Address existing = context.Addresses.Find(inAddress); if (existing == null) { _logger.LogError($"{vin.PrevVOutFetchedAddress} could not be found."); } existing.Balance += change; existing.LastModifiedBlockHeight = (int)tx.Block.Height; UpdateTxIdBlob(existing, tx.TransactionId, tx.Time, existing.Balance, change, context); } }