public TransactionViewModel(Wallet wallet, WalletTransaction transaction, BlockIdentity transactionLocation) { _transaction = transaction; _location = transactionLocation; var groupedOutputs = _transaction.NonChangeOutputs.Select(o => { var destination = wallet.OutputDestination(o); return new GroupedOutput(o.Amount, destination); }).Aggregate(new List<GroupedOutput>(), (items, next) => { var item = items.Find(a => a.Destination == next.Destination); if (item == null) items.Add(next); else item.Amount += next.Amount; return items; }); Depth = BlockChain.Depth(wallet.ChainTip.Height, transactionLocation); Inputs = _transaction.Inputs.Select(input => new Input(-input.Amount, wallet.AccountName(input.PreviousAccount))).ToArray(); Outputs = _transaction.Outputs.Select(output => new Output(output.Amount, wallet.OutputDestination(output))).ToArray(); GroupedOutputs = groupedOutputs; }
private const uint ImportedAccountNumber = 2147483647; // 2**31 - 1 public Wallet(BlockChainIdentity activeChain, TransactionSet txSet, List <AccountProperties> bip0032Accounts, AccountProperties importedAccount, BlockIdentity chainTip) { if (activeChain == null) { throw new ArgumentNullException(nameof(activeChain)); } if (bip0032Accounts == null) { throw new ArgumentNullException(nameof(bip0032Accounts)); } if (importedAccount == null) { throw new ArgumentNullException(nameof(importedAccount)); } if (chainTip == null) { throw new ArgumentNullException(nameof(chainTip)); } _transactionCount = txSet.MinedTransactions.Aggregate(0, (acc, b) => acc + b.Transactions.Count) + txSet.UnminedTransactions.Count; _bip0032Accounts = bip0032Accounts; _importedAccount = importedAccount; var totalBalance = EnumerateAccounts().Aggregate((Amount)0, (acc, a) => acc + a.Item2.TotalBalance); ActiveChain = activeChain; RecentTransactions = txSet; TotalBalance = totalBalance; ChainTip = chainTip; }
public Block(Sha256Hash hash, int height, long unixTime, List<WalletTransaction> transactions) { if (transactions == null) throw new ArgumentNullException(nameof(transactions)); Identity = new BlockIdentity(hash, height); Timestamp = DateTimeOffsetExtras.FromUnixTimeSeconds(unixTime); Transactions = transactions; }
public static int Confirmations(int blockChainHeight, BlockIdentity location) { if (location == null) throw new ArgumentNullException(nameof(location)); if (location.IsUnmined()) return 0; else return blockChainHeight - location.Height + 1; }
public Block(Sha256Hash hash, int height, long unixTime, List <WalletTransaction> transactions) { if (hash == null) { throw new ArgumentNullException(nameof(hash)); } if (transactions == null) { throw new ArgumentNullException(nameof(transactions)); } Identity = new BlockIdentity(hash, height); Timestamp = DateTimeOffsetExtras.FromUnixTimeSeconds(unixTime); Transactions = transactions; }
public async Task <TupleValue <int, BlockIdentity?> > FetchHeadersAsync() { var client = new WalletLoaderService.WalletLoaderServiceClient(_channel); var request = new FetchHeadersRequest(); var response = await client.FetchHeadersAsync(request, cancellationToken : _tokenSource.Token); BlockIdentity?blockIdentity = null; if (response.FetchedHeadersCount != 0) { blockIdentity = new BlockIdentity(new Blake256Hash(response.FirstNewBlockHash.ToByteArray()), response.FirstNewBlockHeight); } return(TupleValue.Create((int)response.FetchedHeadersCount, blockIdentity)); }
public Wallet(BlockChainIdentity activeChain, TransactionSet txSet, Dictionary<Account, AccountState> accounts, BlockIdentity chainTip) { if (accounts == null) throw new ArgumentNullException(nameof(accounts)); if (chainTip == null) throw new ArgumentNullException(nameof(chainTip)); var totalBalance = accounts.Aggregate((Amount)0, (acc, kvp) => acc + kvp.Value.TotalBalance); _transactionCount = txSet.MinedTransactions.Aggregate(0, (acc, b) => acc + b.Transactions.Count) + txSet.UnminedTransactions.Count; _accounts = accounts; ActiveChain = activeChain; RecentTransactions = txSet; TotalBalance = totalBalance; ChainTip = chainTip; }
void InitializeBuffer(Entity entity) { var blockActive = BlockActive[entity]; const bool defaultActive = false; var blockId = BlockId[entity]; var defaultId = new BlockIdentity(0, -1); var blockShape = BlockShape[entity]; const global::UniVox.Types.BlockShape defaultShape = global::UniVox.Types.BlockShape.Cube; var blockMatId = BlockMatId[entity]; var defaultMatId = new ArrayMaterialIdentity(0, -1); var blockSubMatId = BlockSubMatId[entity]; var defaultSubMatId = FaceSubMaterial.CreateAll(-1); var blockCulled = BlockCulled[entity]; const Directions defaultCulled = DirectionsX.AllFlag; for (var i = 0; i < UnivoxDefine.CubeSize; i++) { blockActive[i] = defaultActive; blockId[i] = defaultId; blockShape[i] = defaultShape; blockMatId[i] = defaultMatId; blockSubMatId[i] = defaultSubMatId; blockCulled[i] = defaultCulled; } }
private const uint ImportedAccountNumber = 2147483647; // 2**31 - 1 public Wallet(BlockChainIdentity activeChain, TransactionSet txSet, List<AccountProperties> bip0032Accounts, AccountProperties importedAccount, BlockIdentity chainTip) { if (activeChain == null) throw new ArgumentNullException(nameof(activeChain)); if (bip0032Accounts == null) throw new ArgumentNullException(nameof(bip0032Accounts)); if (importedAccount == null) throw new ArgumentNullException(nameof(importedAccount)); if (chainTip == null) throw new ArgumentNullException(nameof(chainTip)); _transactionCount = txSet.MinedTransactions.Aggregate(0, (acc, b) => acc + b.Transactions.Count) + txSet.UnminedTransactions.Count; _bip0032Accounts = bip0032Accounts; _importedAccount = importedAccount; var totalBalance = EnumerateAccounts().Aggregate((Amount)0, (acc, a) => acc + a.Item2.TotalBalance); ActiveChain = activeChain; RecentTransactions = txSet; TotalBalance = totalBalance; ChainTip = chainTip; }
/// <summary> /// Begins synchronization of the client with the remote wallet process. /// A delegate must be passed to be connected to the wallet's ChangesProcessed event to avoid /// a race where additional notifications are processed in the sync task before the caller /// can connect the event. The caller is responsible for disconnecting the delegate from the /// event handler when finished. /// </summary> /// <param name="walletEventHandler">Event handler for changes to wallet as new transactions are processed.</param> /// <returns>The synced Wallet and the Task that is keeping the wallet in sync.</returns> public async Task<Tuple<Mutex<Wallet>, Task>> Synchronize(EventHandler<Wallet.ChangesProcessedEventArgs> walletEventHandler) { if (walletEventHandler == null) throw new ArgumentNullException(nameof(walletEventHandler)); TransactionNotifications notifications; Task notificationsTask; // TODO: Initialization requests need timeouts. // Loop until synchronization did not race on a reorg. while (true) { // Begin receiving notifications for new and removed wallet transactions before // old transactions are downloaded. Any received notifications are saved to // a buffer and are processed after GetAllTransactionsAsync is awaited. notifications = new TransactionNotifications(_channel, _tokenSource.Token); notificationsTask = notifications.ListenAndBuffer(); var networkTask = NetworkAsync(); var accountsTask = AccountsAsync(); var networkResp = await networkTask; var activeBlockChain = BlockChainIdentity.FromNetworkBits(networkResp.ActiveNetwork); var txSetTask = GetTransactionsAsync(Wallet.MinRecentTransactions, Wallet.NumRecentBlocks(activeBlockChain)); var txSet = await txSetTask; var rpcAccounts = await accountsTask; var lastAccountBlockHeight = rpcAccounts.CurrentBlockHeight; var lastAccountBlockHash = new Blake256Hash(rpcAccounts.CurrentBlockHash.ToByteArray()); var lastTxBlock = txSet.MinedTransactions.LastOrDefault(); if (lastTxBlock != null) { var lastTxBlockHeight = lastTxBlock.Height; var lastTxBlockHash = lastTxBlock.Hash; if (lastTxBlockHeight > lastAccountBlockHeight || (lastTxBlockHeight == lastAccountBlockHeight && !lastTxBlockHash.Equals(lastAccountBlockHash))) { _tokenSource.Cancel(); continue; } } // Read all received notifications thus far and determine if synchronization raced // on a chain reorganize. Try again if so. IList<WalletChanges> transactionNotifications; if (notifications.Buffer.TryReceiveAll(out transactionNotifications)) { if (transactionNotifications.Any(r => r.DetachedBlocks.Count != 0)) { _tokenSource.Cancel(); continue; } // Skip all attached block notifications that are in blocks lower than the // block accounts notification. If blocks exist at or past that height, // the first's hash should equal that from the accounts notification. // // This ensures that both notifications contain data that is valid at this // block. var remainingNotifications = transactionNotifications .SelectMany(r => r.AttachedBlocks) .SkipWhile(b => b.Height < lastAccountBlockHeight) .ToList(); if (remainingNotifications.Count != 0) { if (!remainingNotifications[0].Hash.Equals(lastAccountBlockHash)) { _tokenSource.Cancel(); continue; } } // TODO: Merge remaining notifications with the transaction set. // For now, be lazy and start the whole sync over. if (remainingNotifications.Count > 1) { _tokenSource.Cancel(); continue; } } var accounts = rpcAccounts.Accounts.ToDictionary( a => new Account(a.AccountNumber), a => new AccountProperties { AccountName = a.AccountName, TotalBalance = a.TotalBalance, // TODO: uncomment when added to protospec and implemented by wallet. //ImmatureCoinbaseReward = a.ImmatureBalance, ExternalKeyCount = a.ExternalKeyCount, InternalKeyCount = a.InternalKeyCount, ImportedKeyCount = a.ImportedKeyCount, }); Func<AccountsResponse.Types.Account, AccountProperties> createProperties = a => new AccountProperties { AccountName = a.AccountName, TotalBalance = a.TotalBalance, // TODO: uncomment when added to protospec and implemented by wallet. //ImmatureCoinbaseReward = a.ImmatureBalance, ExternalKeyCount = a.ExternalKeyCount, InternalKeyCount = a.InternalKeyCount, ImportedKeyCount = a.ImportedKeyCount, }; // This assumes that all but the last account listed in the RPC response are // BIP0032 accounts, with the same account number as their List index. var bip0032Accounts = rpcAccounts.Accounts.Take(rpcAccounts.Accounts.Count - 1).Select(createProperties).ToList(); var importedAccount = createProperties(rpcAccounts.Accounts.Last()); var chainTip = new BlockIdentity(lastAccountBlockHash, lastAccountBlockHeight); var wallet = new Wallet(activeBlockChain, txSet, bip0032Accounts, importedAccount, chainTip); wallet.ChangesProcessed += walletEventHandler; var walletMutex = new Mutex<Wallet>(wallet); var syncTask = Task.Run(async () => { var client = new WalletService.WalletServiceClient(_channel); var accountsStream = client.AccountNotifications(new AccountNotificationsRequest(), cancellationToken: _tokenSource.Token); var accountChangesTask = accountsStream.ResponseStream.MoveNext(); var txChangesTask = notifications.Buffer.OutputAvailableAsync(); while (true) { var completedTask = await Task.WhenAny(accountChangesTask, txChangesTask); if (!await completedTask) { break; } using (var walletGuard = await walletMutex.LockAsync()) { var w = walletGuard.Instance; if (completedTask == accountChangesTask) { var accountProperties = accountsStream.ResponseStream.Current; var account = new Account(accountProperties.AccountNumber); w.UpdateAccountProperties(account, accountProperties.AccountName, accountProperties.ExternalKeyCount, accountProperties.InternalKeyCount, accountProperties.ImportedKeyCount); accountChangesTask = accountsStream.ResponseStream.MoveNext(); } else if (completedTask == txChangesTask) { var changes = notifications.Buffer.Receive(); w.ApplyTransactionChanges(changes); txChangesTask = notifications.Buffer.OutputAvailableAsync(); } } } await notificationsTask; }); return Tuple.Create(walletMutex, syncTask); } }
public async Task<TupleValue<int, BlockIdentity?>> FetchHeadersAsync() { var client = new WalletLoaderService.WalletLoaderServiceClient(_channel); var request = new FetchHeadersRequest(); var response = await client.FetchHeadersAsync(request, cancellationToken: _tokenSource.Token); BlockIdentity? blockIdentity = null; if (response.FetchedHeadersCount != 0) { blockIdentity = new BlockIdentity(new Blake256Hash(response.FirstNewBlockHash.ToByteArray()), response.FirstNewBlockHeight); } return TupleValue.Create((int)response.FetchedHeadersCount, blockIdentity); }
void InitializeBuffer(Entity entity) { var buffers = CreateBuffers(); var blockActive = buffers._blockActiveAccessor[entity]; const bool defaultActive = false; var blockId = buffers._blockIdAccessor[entity]; var defaultId = new BlockIdentity(0, -1); var blockShape = buffers._blockShapeAccessor[entity]; const global::UniVox.Types.BlockShape defaultShape = global::UniVox.Types.BlockShape.Cube; var blockMatId = buffers._blockMatIdAccessor[entity]; var defaultMatId = new ArrayMaterialIdentity(0, -1); var blockSubMatId = buffers._blockSubMatIdAccessor[entity]; var defaultSubMatId = FaceSubMaterial.CreateAll(-1); var blockCulled = buffers._blockCulledAccessor[entity]; const Directions defaultCulled = DirectionsX.AllFlag; for (var i = 0; i < UnivoxDefine.CubeSize; i++) { blockActive[i] = defaultActive; blockId[i] = defaultId; blockShape[i] = defaultShape; blockMatId[i] = defaultMatId; blockSubMatId[i] = defaultSubMatId; blockCulled[i] = defaultCulled; } // void CreateChunk(VoxelWorld world, int3 chunkPos) // { // if (world.ContainsKey(chunkPos)) // { // Debug.Log($"Chunk {chunkPos} already exists!"); // return; // } // // var blockReg = GameManager.Registry.Blocks; // // // if (!blockReg.TryGetIdentity(BaseGameMod.GrassBlock, out var grass)) // throw new AssetNotFoundException(BaseGameMod.GrassBlock.ToString()); // if (!blockReg.TryGetIdentity(BaseGameMod.DirtBlock, out var dirt)) // throw new AssetNotFoundException(BaseGameMod.DirtBlock.ToString()); // if (!blockReg.TryGetIdentity(BaseGameMod.StoneBlock, out var stone)) // throw new AssetNotFoundException(BaseGameMod.StoneBlock.ToString()); // if (!blockReg.TryGetIdentity(BaseGameMod.SandBlock, out var sand)) // throw new AssetNotFoundException(BaseGameMod.SandBlock.ToString()); // // var em = world.EntityManager; // var entityArchetype = world.EntityManager.CreateArchetype( // typeof(ChunkIdComponent), // typeof(BlockActiveComponent), typeof(BlockIdentityComponent), // typeof(BlockShapeComponent), typeof(BlockMaterialIdentityComponent), // typeof(BlockSubMaterialIdentityComponent), typeof(BlockCulledFacesComponent) // ); // // var entity = world.GetOrCreate(chunkPos, entityArchetype); // EnforceChunkSize(em, entity); // // world.EntityManager.SetComponentData(entity, // new ChunkIdComponent() {Value = new UniversalChunkId(0, chunkPos)}); // // // var activeArray = em.GetBuffer<BlockActiveComponent>(entity); // var blockIdentities = em.GetBuffer<BlockIdentityComponent>(entity); // var blockMaterials = em.GetBuffer<BlockMaterialIdentityComponent>(entity); // var blockShapes = em.GetBuffer<BlockShapeComponent>(entity); // var culledFaces = em.GetBuffer<BlockCulledFacesComponent>(entity); // // var invalidMaterial = new ArrayMaterialIdentity(0, -1); // // // for (var i = 0; i < UnivoxDefine.CubeSize; i++) // { // var pos = UnivoxUtil.GetPosition3(i); // // var xTop = (pos.x == UnivoxDefine.AxisSize - 1); // var yTop = (pos.y == UnivoxDefine.AxisSize - 1); // var zTop = (pos.z == UnivoxDefine.AxisSize - 1); // // var xBot = (pos.x == 0); // var yBot = (pos.y == 0); // var zBot = (pos.z == 0); // // activeArray[i] = true; // // // blockMaterials[i] = invalidMaterial; // // if (!yTop) // { // if (xTop && !zTop) // { // blockIdentities[i] = stone; // } // else if (!xTop && zTop) // { // blockIdentities[i] = sand; // } // else // { // blockIdentities[i] = dirt; // } // } // // else // blockIdentities[i] = grass; // // // blockShapes[i] = BlockShape.Cube; // // if (xTop || yTop || zTop || xBot || yBot || zBot) // { // var revealed = DirectionsX.NoneFlag; // // if (xTop) // revealed |= Directions.Right; // else if (xBot) // revealed |= Directions.Left; // // // if (yTop) // revealed |= Directions.Up; // else if (yBot) // revealed |= Directions.Down; // // if (zTop) // revealed |= Directions.Forward; // else if (zBot) // revealed |= Directions.Backward; // // culledFaces[i] = ~revealed; // } // else // culledFaces[i] = DirectionsX.AllFlag; // } // } }
/// <summary> /// Begins synchronization of the client with the remote wallet process. /// A delegate must be passed to be connected to the wallet's ChangesProcessed event to avoid /// a race where additional notifications are processed in the sync task before the caller /// can connect the event. The caller is responsible for disconnecting the delegate from the /// event handler when finished. /// </summary> /// <param name="walletEventHandler">Event handler for changes to wallet as new transactions are processed.</param> /// <returns>The synced Wallet and the Task that is keeping the wallet in sync.</returns> public async Task <Tuple <Wallet, Task> > Synchronize(EventHandler <Wallet.ChangesProcessedEventArgs> walletEventHandler) { if (walletEventHandler == null) { throw new ArgumentNullException(nameof(walletEventHandler)); } TransactionNotifications notifications; Task notificationsTask; // TODO: Initialization requests need timeouts. // Loop until synchronization did not race on a reorg. while (true) { // Begin receiving notifications for new and removed wallet transactions before // old transactions are downloaded. Any received notifications are saved to // a buffer and are processed after GetAllTransactionsAsync is awaited. notifications = new TransactionNotifications(_channel, _tokenSource.Token); notificationsTask = notifications.ListenAndBuffer(); var networkTask = NetworkAsync(); var accountsTask = AccountsAsync(); var networkResp = await networkTask; var activeBlockChain = BlockChainIdentity.FromNetworkBits(networkResp.ActiveNetwork); var txSetTask = GetTransactionsAsync(Wallet.MinRecentTransactions, Wallet.NumRecentBlocks(activeBlockChain)); var txSet = await txSetTask; var rpcAccounts = await accountsTask; var lastAccountBlockHeight = rpcAccounts.CurrentBlockHeight; var lastAccountBlockHash = new Blake256Hash(rpcAccounts.CurrentBlockHash.ToByteArray()); var lastTxBlock = txSet.MinedTransactions.LastOrDefault(); if (lastTxBlock != null) { var lastTxBlockHeight = lastTxBlock.Height; var lastTxBlockHash = lastTxBlock.Hash; if (lastTxBlockHeight > lastAccountBlockHeight || (lastTxBlockHeight == lastAccountBlockHeight && !lastTxBlockHash.Equals(lastAccountBlockHash))) { _tokenSource.Cancel(); continue; } } // Read all received notifications thus far and determine if synchronization raced // on a chain reorganize. Try again if so. IList <WalletChanges> transactionNotifications; if (notifications.Buffer.TryReceiveAll(out transactionNotifications)) { if (transactionNotifications.Any(r => r.DetachedBlocks.Count != 0)) { _tokenSource.Cancel(); continue; } // Skip all attached block notifications that are in blocks lower than the // block accounts notification. If blocks exist at or past that height, // the first's hash should equal that from the accounts notification. // // This ensures that both notifications contain data that is valid at this // block. var remainingNotifications = transactionNotifications .SelectMany(r => r.AttachedBlocks) .SkipWhile(b => b.Height < lastAccountBlockHeight) .ToList(); if (remainingNotifications.Count != 0) { if (!remainingNotifications[0].Hash.Equals(lastAccountBlockHash)) { _tokenSource.Cancel(); continue; } } // TODO: Merge remaining notifications with the transaction set. // For now, be lazy and start the whole sync over. if (remainingNotifications.Count > 1) { _tokenSource.Cancel(); continue; } } var accounts = rpcAccounts.Accounts.ToDictionary( a => new Account(a.AccountNumber), a => new AccountProperties { AccountName = a.AccountName, TotalBalance = a.TotalBalance, // TODO: uncomment when added to protospec and implemented by wallet. //ImmatureCoinbaseReward = a.ImmatureBalance, ExternalKeyCount = a.ExternalKeyCount, InternalKeyCount = a.InternalKeyCount, ImportedKeyCount = a.ImportedKeyCount, }); Func <AccountsResponse.Types.Account, AccountProperties> createProperties = a => new AccountProperties { AccountName = a.AccountName, TotalBalance = a.TotalBalance, // TODO: uncomment when added to protospec and implemented by wallet. //ImmatureCoinbaseReward = a.ImmatureBalance, ExternalKeyCount = a.ExternalKeyCount, InternalKeyCount = a.InternalKeyCount, ImportedKeyCount = a.ImportedKeyCount, }; // This assumes that all but the last account listed in the RPC response are // BIP0032 accounts, with the same account number as their List index. var bip0032Accounts = rpcAccounts.Accounts.Take(rpcAccounts.Accounts.Count - 1).Select(createProperties).ToList(); var importedAccount = createProperties(rpcAccounts.Accounts.Last()); var chainTip = new BlockIdentity(lastAccountBlockHash, lastAccountBlockHeight); var wallet = new Wallet(activeBlockChain, txSet, bip0032Accounts, importedAccount, chainTip); wallet.ChangesProcessed += walletEventHandler; var syncTask = Task.Run(async() => { var client = WalletService.NewClient(_channel); var accountsStream = client.AccountNotifications(new AccountNotificationsRequest(), cancellationToken: _tokenSource.Token); var accountChangesTask = accountsStream.ResponseStream.MoveNext(); var txChangesTask = notifications.Buffer.OutputAvailableAsync(); while (true) { var completedTask = await Task.WhenAny(accountChangesTask, txChangesTask); if (!await completedTask) { break; } if (completedTask == accountChangesTask) { var accountProperties = accountsStream.ResponseStream.Current; var account = new Account(accountProperties.AccountNumber); wallet.UpdateAccountProperties(account, accountProperties.AccountName, accountProperties.ExternalKeyCount, accountProperties.InternalKeyCount, accountProperties.ImportedKeyCount); accountChangesTask = accountsStream.ResponseStream.MoveNext(); } else if (completedTask == txChangesTask) { var changes = notifications.Buffer.Receive(); wallet.ApplyTransactionChanges(changes); } } await notificationsTask; }); return(Tuple.Create(wallet, syncTask)); } }
public LatestBlockPeers(BlockIdentity block, PeerConnection[] connections) { Block = block; Connections = connections; }
protected bool Equals(BlockIdentity other) { return(Height == other.Height && Equals(Hash, other.Hash)); }
void Update() { // if (Input.GetKeyDown(KeyCode.UpArrow)) // { // id++; // // // id %= idLimit; // } // else if (Input.GetKeyDown(KeyCode.DownArrow)) // { // id--; // id += idLimit; // // // id %= idLimit; // } if (Input.GetMouseButtonDown(0)) { if (EventSystem.current.IsPointerOverGameObject()) { return; } const float distance = UnivoxDefine.AxisSize * 8; //Raycast at least 8 chunks away var camRay = _camera.ScreenPointToRay(Input.mousePosition); var start = camRay.origin; var direction = camRay.direction; var input = new RaycastInput() { Start = start, End = start + direction * distance, Filter = CollisionFilter.Default }; _lastRay = input; if (mode == ClickMode.Alter) { _lastRay = input; if (VoxelRaycast(input, out var hit, out var voxelInfo)) { var em = voxelInfo.World.EntityManager; var blockIdentityArray = em.GetBuffer <BlockIdentityComponent>(voxelInfo.ChunkEntity); // em.DirtyComponent<BlockActiveComponent.Version>(entity); em.DirtyComponent <BlockIdentityComponent.Version>(voxelInfo.ChunkEntity); blockIdentityArray[voxelInfo.BlockIndex] = new BlockIdentity() { Mod = 0, Block = id }; // accessorInfo.Version.Dirty(); // accessorRender.Version.Dirty(); // BlockChanged.NotifyEntity(voxelInfo.ChunkEntity, voxelInfo.World.EntityManager, // (short) voxelInfo.BlockIndex); _lastVoxel = voxelInfo.WorldPosition; _hitPoint = hit.Position; // Debug.Log($"Hit Alter : {voxelInfo.BlockPosition}"); } else { Debug.Log($"Missed Alter : {hit.Position} -> {hit.SurfaceNormal}"); } } else if (mode == ClickMode.Place) { if (VoxelRaycast(input, out var hit, out var voxelInfo)) { var em = voxelInfo.World.EntityManager; var blockPos = voxelInfo.BlockPosition + new int3(hit.SurfaceNormal); var blockIndex = UnivoxUtil.GetIndex(blockPos); if (UnivoxUtil.IsPositionValid(blockPos)) { var blockActiveArray = em.GetBuffer <BlockActiveComponent>(voxelInfo.ChunkEntity); var blockIdentityArray = em.GetBuffer <BlockIdentityComponent>(voxelInfo.ChunkEntity); em.DirtyComponent <BlockIdentityComponent.Version>(voxelInfo.ChunkEntity); em.DirtyComponent <BlockActiveComponent.Version>(voxelInfo.ChunkEntity); blockActiveArray[blockIndex] = new BlockActiveComponent() { Value = true }; blockIdentityArray[blockIndex] = new BlockIdentity() { Mod = 0, Block = id }; _lastVoxel = UnivoxUtil.ToWorldPosition(voxelInfo.ChunkPosition, blockPos); _hitPoint = hit.Position; } else { Debug.Log($"OOB Create : {hit.Position} -> {blockPos} -> {hit.SurfaceNormal}"); } } else { Debug.Log($"Missed Create : {hit.Position} -> {hit.SurfaceNormal}"); } }
public void ApplyTransactionChanges(WalletChanges changes) { if (changes == null) throw new ArgumentNullException(nameof(changes)); // A reorganize cannot be handled if the number of removed blocks exceeds the // minimum number saved in memory. if (changes.DetachedBlocks.Count >= NumRecentBlocks(ActiveChain)) throw new BlockChainConsistencyException("Reorganize too deep"); var newChainTip = changes.AttachedBlocks.LastOrDefault(); if (ChainTip.Height >= newChainTip?.Height) { var msg = $"New chain tip {newChainTip.Hash} (height {newChainTip.Height}) neither extends nor replaces " + $"the current chain (currently synced to hash {ChainTip.Hash}, height {ChainTip.Height})"; throw new BlockChainConsistencyException(msg); } if (changes.NewUnminedTransactions.Any(tx => !changes.AllUnminedHashes.Contains(tx.Hash))) throw new BlockChainConsistencyException("New unmined transactions contains tx with hash not found in all unmined transaction hash set"); var eventArgs = new ChangesProcessedEventArgs(); var reorgedBlocks = RecentTransactions.MinedTransactions .ReverseList() .TakeWhile(b => changes.DetachedBlocks.Contains(b.Hash)) .ToList(); var numReorgedBlocks = reorgedBlocks.Count; foreach (var reorgedTx in reorgedBlocks.SelectMany(b => b.Transactions)) { if (BlockChain.IsCoinbase(reorgedTx.Transaction) || !changes.AllUnminedHashes.Contains(reorgedTx.Hash)) { RemoveTransactionFromTotals(reorgedTx, eventArgs.ModifiedAccountProperties); } else { RecentTransactions.UnminedTransactions[reorgedTx.Hash] = reorgedTx; eventArgs.MovedTransactions.Add(reorgedTx.Hash, BlockIdentity.Unmined); } } var numRemoved = RecentTransactions.MinedTransactions.RemoveAll(block => changes.DetachedBlocks.Contains(block.Hash)); if (numRemoved != numReorgedBlocks) { throw new BlockChainConsistencyException("Number of blocks removed exceeds those for which transactions were removed"); } foreach (var block in changes.AttachedBlocks.Where(b => b.Transactions.Count > 0)) { RecentTransactions.MinedTransactions.Add(block); foreach (var tx in block.Transactions) { if (RecentTransactions.UnminedTransactions.ContainsKey(tx.Hash)) { RecentTransactions.UnminedTransactions.Remove(tx.Hash); eventArgs.MovedTransactions[tx.Hash] = block.Identity; } else if (!eventArgs.MovedTransactions.ContainsKey(tx.Hash)) { AddTransactionToTotals(tx, eventArgs.ModifiedAccountProperties); eventArgs.AddedTransactions.Add(Tuple.Create(tx, block.Identity)); } } } // TODO: What about new transactions which were not added in a newly processed // block (e.g. importing an address and rescanning for outputs)? foreach (var tx in changes.NewUnminedTransactions.Where(tx => !RecentTransactions.UnminedTransactions.ContainsKey(tx.Hash))) { RecentTransactions.UnminedTransactions[tx.Hash] = tx; AddTransactionToTotals(tx, eventArgs.ModifiedAccountProperties); // TODO: When reorgs are handled, this will need to check whether the transaction // being added to the unmined collection was previously in a block. eventArgs.AddedTransactions.Add(Tuple.Create(tx, BlockIdentity.Unmined)); } var removedUnmined = RecentTransactions.UnminedTransactions .Where(kvp => !changes.AllUnminedHashes.Contains(kvp.Key)) .ToList(); // Collect to list so UnminedTransactions can be modified below. foreach (var unmined in removedUnmined) { // Transactions that were mined rather than being removed from the unmined // set due to a conflict have already been removed. RecentTransactions.UnminedTransactions.Remove(unmined.Key); RemoveTransactionFromTotals(unmined.Value, eventArgs.ModifiedAccountProperties); eventArgs.RemovedTransactions.Add(unmined.Value); } if (newChainTip != null) { ChainTip = newChainTip.Identity; eventArgs.NewChainTip = newChainTip.Identity; } OnChangesProcessed(eventArgs); }
public BlockPeers(BlockIdentity block, IEnumerable <PeerConnection> peers) { Block = block; Peers = peers; }
public Wallet(BlockChainIdentity activeChain, TransactionSet txSet, Dictionary <Account, AccountProperties> accounts, BlockIdentity chainTip) { if (activeChain == null) { throw new ArgumentNullException(nameof(activeChain)); } if (accounts == null) { throw new ArgumentNullException(nameof(accounts)); } if (chainTip == null) { throw new ArgumentNullException(nameof(chainTip)); } var totalBalance = accounts.Aggregate((Amount)0, (acc, kvp) => acc + kvp.Value.TotalBalance); _transactionCount = txSet.MinedTransactions.Aggregate(0, (acc, b) => acc + b.Transactions.Count) + txSet.UnminedTransactions.Count; _accounts = accounts; ActiveChain = activeChain; RecentTransactions = txSet; TotalBalance = totalBalance; ChainTip = chainTip; }
public void ApplyTransactionChanges(WalletChanges changes) { if (changes == null) { throw new ArgumentNullException(nameof(changes)); } // A reorganize cannot be handled if the number of removed blocks exceeds the // minimum number saved in memory. if (changes.DetachedBlocks.Count >= NumRecentBlocks) { throw new BlockChainConsistencyException("Reorganize too deep"); } var newChainTip = changes.AttachedBlocks.LastOrDefault(); if (ChainTip.Height >= newChainTip?.Height) { var msg = $"New chain tip {newChainTip.Hash} (height {newChainTip.Height}) neither extends nor replaces " + $"the current chain (currently synced to hash {ChainTip.Hash}, height {ChainTip.Height})"; throw new BlockChainConsistencyException(msg); } if (changes.NewUnminedTransactions.Any(tx => !changes.AllUnminedHashes.Contains(tx.Hash))) { throw new BlockChainConsistencyException("New unmined transactions contains tx with hash not found in all unmined transaction hash set"); } var eventArgs = new ChangesProcessedEventArgs(); var reorgedBlocks = RecentTransactions.MinedTransactions .ReverseList() .TakeWhile(b => changes.DetachedBlocks.Contains(b.Hash)) .ToList(); var numReorgedBlocks = reorgedBlocks.Count; foreach (var reorgedTx in reorgedBlocks.SelectMany(b => b.Transactions)) { if (BlockChain.IsCoinbase(reorgedTx.Transaction) || !changes.AllUnminedHashes.Contains(reorgedTx.Hash)) { RemoveTransactionFromTotals(reorgedTx, eventArgs.ModifiedAccountProperties); } else { RecentTransactions.UnminedTransactions[reorgedTx.Hash] = reorgedTx; eventArgs.MovedTransactions.Add(reorgedTx.Hash, BlockIdentity.Unmined); } } var numRemoved = RecentTransactions.MinedTransactions.RemoveAll(block => changes.DetachedBlocks.Contains(block.Hash)); if (numRemoved != numReorgedBlocks) { throw new BlockChainConsistencyException("Number of blocks removed exceeds those for which transactions were removed"); } foreach (var block in changes.AttachedBlocks.Where(b => b.Transactions.Count > 0)) { RecentTransactions.MinedTransactions.Add(block); foreach (var tx in block.Transactions) { if (RecentTransactions.UnminedTransactions.ContainsKey(tx.Hash)) { RecentTransactions.UnminedTransactions.Remove(tx.Hash); eventArgs.MovedTransactions[tx.Hash] = block.Identity; } else if (!eventArgs.MovedTransactions.ContainsKey(tx.Hash)) { AddTransactionToTotals(tx, eventArgs.ModifiedAccountProperties); eventArgs.AddedTransactions.Add(Tuple.Create(tx, block.Identity)); } } } // TODO: What about new transactions which were not added in a newly processed // block (e.g. importing an address and rescanning for outputs)? foreach (var tx in changes.NewUnminedTransactions.Where(tx => !RecentTransactions.UnminedTransactions.ContainsKey(tx.Hash))) { RecentTransactions.UnminedTransactions[tx.Hash] = tx; AddTransactionToTotals(tx, eventArgs.ModifiedAccountProperties); // TODO: When reorgs are handled, this will need to check whether the transaction // being added to the unmined collection was previously in a block. eventArgs.AddedTransactions.Add(Tuple.Create(tx, BlockIdentity.Unmined)); } var removedUnmined = RecentTransactions.UnminedTransactions .Where(kvp => !changes.AllUnminedHashes.Contains(kvp.Key)) .ToList(); // Collect to list so UnminedTransactions can be modified below. foreach (var unmined in removedUnmined) { // Transactions that were mined rather than being removed from the unmined // set due to a conflict have already been removed. RecentTransactions.UnminedTransactions.Remove(unmined.Key); RemoveTransactionFromTotals(unmined.Value, eventArgs.ModifiedAccountProperties); eventArgs.RemovedTransactions.Add(unmined.Value); } if (newChainTip != null) { ChainTip = newChainTip.Identity; eventArgs.NewChainTip = newChainTip.Identity; } OnChangesProcessed(eventArgs); }