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;
        }
Beispiel #2
0
        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;
        }
Beispiel #3
0
        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;
        }
Beispiel #4
0
        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;
        }
Beispiel #5
0
        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;
        }
Beispiel #6
0
        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));
        }
Beispiel #7
0
        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;
        }
Beispiel #8
0
            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;
                }
            }
Beispiel #9
0
        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;
        }
Beispiel #10
0
        /// <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);
            }
        }
Beispiel #11
0
 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);
 }
Beispiel #12
0
        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;
//            }
//        }
        }
Beispiel #13
0
        /// <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;
 }
Beispiel #15
0
 protected bool Equals(BlockIdentity other)
 {
     return(Height == other.Height && Equals(Hash, other.Hash));
 }
Beispiel #16
0
        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}");
                    }
                }
Beispiel #17
0
        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);
        }
Beispiel #18
0
 public BlockPeers(BlockIdentity block, IEnumerable <PeerConnection> peers)
 {
     Block = block;
     Peers = peers;
 }
Beispiel #19
0
        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;
        }
Beispiel #20
0
        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);
        }