Beispiel #1
0
        internal void AddBlock(Chain chain, Block block)
        {
            if (!Capabilities.HasFlag(PeerCaps.Sync))
            {
                return;
            }

            foreach (var peer in _peers.Values)
            {
                var msg = new ListMessage(this.Keys.Address, this.PublicEndpoint, RequestKind.Blocks);
                msg.AddBlockRange(chain, block.Height, 1);

                SendMessage(peer, msg);
            }
        }
Beispiel #2
0
        private Message HandleMessage(Peer peer, Message msg)
        {
            if (msg.IsSigned && msg.Address != Address.Null)
            {
                peer.SetAddress(msg.Address);
            }
            else
            {
                return(new ErrorMessage(Address, P2PError.MessageShouldBeSigned));
            }

            switch (msg.Opcode)
            {
            case Opcode.EVENT:
            {
                var evtMessage = (EventMessage)msg;
                var evt        = evtMessage.Event;
                Logger.Message("New event: " + evt.ToString());
                return(null);
            }

            case Opcode.REQUEST:
            {
                var request = (RequestMessage)msg;

                if (request.NexusName != Nexus.Name)
                {
                    return(new ErrorMessage(Address, P2PError.InvalidNexus));
                }

                if (request.Kind == RequestKind.None)
                {
                    return(null);
                }

                var answer = new ListMessage(this.Address, request.Kind);

                if (request.Kind.HasFlag(RequestKind.Peers))
                {
                    answer.SetPeers(this.Peers.Where(x => x != peer).Select(x => x.Endpoint));
                }

                if (request.Kind.HasFlag(RequestKind.Chains))
                {
                    var chains = Nexus.Chains.Select(x => Nexus.FindChainByName(x)).Select(x => new ChainInfo(x.Name, Nexus.GetParentChainByName(x.Name), x.LastBlock != null ? x.LastBlock.Height : 0));
                    answer.SetChains(chains);
                }

                if (request.Kind.HasFlag(RequestKind.Mempool))
                {
                    var txs = _mempool.GetTransactions().Select(x => Base16.Encode(x.ToByteArray(true)));
                    answer.SetMempool(txs);
                }

                if (request.Kind.HasFlag(RequestKind.Blocks))
                {
                    foreach (var entry in request.Blocks)
                    {
                        var chain = this.Nexus.FindChainByName(entry.Key);
                        if (chain == null)
                        {
                            continue;
                        }

                        var startBlock = entry.Value;
                        if (startBlock > chain.BlockHeight)
                        {
                            continue;
                        }

                        var blockList    = new List <string>();
                        var currentBlock = startBlock;
                        while (blockList.Count < 50 && currentBlock <= chain.BlockHeight)
                        {
                            var block = chain.FindBlockByHeight(currentBlock);
                            var bytes = block.ToByteArray();
                            var str   = Base16.Encode(bytes);

                            foreach (var tx in chain.GetBlockTransactions(block))
                            {
                                var txBytes = tx.ToByteArray(true);
                                str += "/" + Base16.Encode(txBytes);
                            }

                            blockList.Add(str);
                            currentBlock++;
                        }

                        answer.AddBlockRange(chain.Name, startBlock, blockList);
                    }
                }

                return(answer);
            }

            case Opcode.LIST:
            {
                var listMsg = (ListMessage)msg;

                var outKind = RequestKind.None;

                if (listMsg.Kind.HasFlag(RequestKind.Peers))
                {
                    var newPeers = listMsg.Peers.Where(x => !IsKnown(x));
                    foreach (var entry in listMsg.Peers)
                    {
                        Logger.Message("New peer: " + entry.ToString());
                    }
                    QueueEndpoints(newPeers);
                }

                var blockFetches = new Dictionary <string, uint>();
                if (listMsg.Kind.HasFlag(RequestKind.Chains))
                {
                    foreach (var entry in listMsg.Chains)
                    {
                        var chain = Nexus.FindChainByName(entry.name);
                        // NOTE if we dont find this chain then it is too soon for ask for blocks from that chain
                        if (chain != null && chain.BlockHeight < entry.height)
                        {
                            blockFetches[entry.name] = chain.BlockHeight + 1;
                        }
                    }
                }

                if (listMsg.Kind.HasFlag(RequestKind.Mempool))
                {
                    int submittedCount = 0;
                    foreach (var txStr in listMsg.Mempool)
                    {
                        var bytes = Base16.Decode(txStr);
                        var tx    = Transaction.Unserialize(bytes);
                        try
                        {
                            _mempool.Submit(tx);
                            submittedCount++;
                        }
                        catch
                        {
                        }

                        Logger.Message(submittedCount + " new transactions");
                    }
                }

                if (listMsg.Kind.HasFlag(RequestKind.Blocks))
                {
                    bool addedBlocks = false;

                    foreach (var entry in listMsg.Blocks)
                    {
                        var chain = Nexus.FindChainByName(entry.Key);
                        if (chain == null)
                        {
                            continue;
                        }

                        var blockRange   = entry.Value;
                        var currentBlock = blockRange.startHeight;
                        foreach (var rawBlock in blockRange.rawBlocks)
                        {
                            var temp = rawBlock.Split('/');

                            var block = Block.Unserialize(Base16.Decode(temp[0]));

                            var transactions = new List <Transaction>();
                            for (int i = 1; i < temp.Length; i++)
                            {
                                var tx = Transaction.Unserialize(Base16.Decode(temp[i]));
                                transactions.Add(tx);
                            }

                            // TODO this wont work in the future...
                            try
                            {
                                chain.AddBlock(block, transactions, null);

                                foreach (var hash in block.TransactionHashes)
                                {
                                    var events = block.GetEventsForTransaction(hash);
                                    foreach (var evt in events)
                                    {
                                        AddEvent(evt);
                                    }
                                }
                            }
                            catch (Exception e)
                            {
                                throw new Exception("block add failed");
                            }

                            Logger.Message($"Added block #{currentBlock} to {chain.Name}");
                            addedBlocks = true;
                            currentBlock++;
                        }
                    }

                    if (addedBlocks)
                    {
                        outKind |= RequestKind.Chains;
                    }
                }

                if (blockFetches.Count > 0)
                {
                    outKind |= RequestKind.Blocks;
                }

                if (outKind != RequestKind.None)
                {
                    var answer = new RequestMessage(outKind, Nexus.Name, this.Address);

                    if (blockFetches.Count > 0)
                    {
                        answer.SetBlocks(blockFetches);
                    }

                    return(answer);
                }

                break;
            }

            case Opcode.MEMPOOL_Add:
            {
                var memtx    = (MempoolAddMessage)msg;
                var prevSize = _mempool.Size;
                foreach (var tx in memtx.Transactions)
                {
                    _mempool.Submit(tx);
                }
                var count = _mempool.Size - prevSize;
                Logger.Message($"Added {count} txs to the mempool");
                break;
            }

            case Opcode.BLOCKS_List:
            {
                break;
            }

            case Opcode.ERROR:
            {
                var errorMsg = (ErrorMessage)msg;
                if (string.IsNullOrEmpty(errorMsg.Text))
                {
                    Logger.Error($"ERROR: {errorMsg.Code}");
                }
                else
                {
                    Logger.Error($"ERROR: {errorMsg.Code} ({errorMsg.Text})");
                }
                break;
            }
            }

            Logger.Message("No answer sent.");
            return(null);
        }
Beispiel #3
0
        private Message HandleMessage(Peer peer, Message msg)
        {
            if (msg.IsSigned && !msg.Address.IsNull)
            {
                if (msg.Address.IsUser)
                {
                    peer.SetAddress(msg.Address);
                }
                else
                {
                    return(new ErrorMessage(Address, this.PublicEndpoint, P2PError.InvalidAddress));
                }
            }
            else
            {
                return(new ErrorMessage(Address, this.PublicEndpoint, P2PError.MessageShouldBeSigned));
            }

            Endpoint endpoint;

            try
            {
                endpoint = Endpoint.FromString(msg.Host);
            }
            catch (ChainException e)
            {
                return(new ErrorMessage(Address, this.PublicEndpoint, P2PError.InvalidEndpoint));
            }

            var peerKey = endpoint.ToString();

            lock (_peers)
            {
                if (!_peers.ContainsKey(peerKey))
                {
                    Logger.Message("Added peer: " + peerKey);
                    peer.UpdateEndpoint(endpoint);
                    _peers[peerKey] = peer;
                }
            }

            switch (msg.Opcode)
            {
            case Opcode.EVENT:
            {
                var evtMessage = (EventMessage)msg;
                var evt        = evtMessage.Event;
                Logger.Message("New event: " + evt.ToString());
                return(null);
            }

            case Opcode.REQUEST:
            {
                var request = (RequestMessage)msg;

                if (request.NexusName != Nexus.Name)
                {
                    return(new ErrorMessage(Address, this.PublicEndpoint, P2PError.InvalidNexus));
                }

                if (request.Kind == RequestKind.None)
                {
                    return(null);
                }

                var answer = new ListMessage(this.Address, this.PublicEndpoint, request.Kind);

                if (request.Kind.HasFlag(RequestKind.Peers))
                {
                    answer.SetPeers(this.Peers.Where(x => x != peer).Select(x => x.Endpoint.ToString()));
                }

                if (request.Kind.HasFlag(RequestKind.Chains))
                {
                    var chainList = Nexus.GetChains(Nexus.RootStorage);
                    var chains    = chainList.Select(x => Nexus.GetChainByName(x)).Select(x => new ChainInfo(x.Name, Nexus.GetParentChainByName(x.Name), x.Height)).ToArray();
                    answer.SetChains(chains);
                }

                if (request.Kind.HasFlag(RequestKind.Mempool) && Capabilities.HasFlag(PeerCaps.Mempool))
                {
                    var txs = _mempool.GetTransactions().Select(x => Base16.Encode(x.ToByteArray(true)));
                    answer.SetMempool(txs);
                }

                if (request.Kind.HasFlag(RequestKind.Blocks))
                {
                    foreach (var entry in request.Blocks)
                    {
                        var chain = this.Nexus.GetChainByName(entry.Key);
                        if (chain == null)
                        {
                            continue;
                        }

                        answer.AddBlockRange(chain, entry.Value);
                    }
                }

                return(answer);
            }

            case Opcode.LIST:
            {
                var listMsg = (ListMessage)msg;

                var outKind = RequestKind.None;

                if (listMsg.Kind.HasFlag(RequestKind.Peers))
                {
                    IEnumerable <string> newPeers;

                    lock (_peers)
                    {
                        newPeers = listMsg.Peers.Where(x => !_peers.ContainsKey(x));
                    }

                    foreach (var entry in listMsg.Peers)
                    {
                        Logger.Message("New peer: " + entry.ToString());
                    }
                    QueueEndpoints(newPeers);
                }

                var blockFetches = new Dictionary <string, RequestRange>();
                if (listMsg.Kind.HasFlag(RequestKind.Chains))
                {
                    foreach (var entry in listMsg.Chains)
                    {
                        var chain = Nexus.GetChainByName(entry.name);
                        // NOTE if we dont find this chain then it is too soon for ask for blocks from that chain
                        if (chain != null && chain.Height < entry.height)
                        {
                            var start = chain.Height + 1;
                            var end   = entry.height;
                            var limit = start + ListMessage.MaxBlocks - 1;

                            if (end > limit)
                            {
                                end = limit;
                            }

                            blockFetches[entry.name] = new RequestRange(start, end);

                            lock (_knownHeights)
                            {
                                _knownHeights[chain.Name] = entry.height;
                            }
                        }
                    }
                }

                if (listMsg.Kind.HasFlag(RequestKind.Mempool) && Capabilities.HasFlag(PeerCaps.Mempool))
                {
                    int submittedCount = 0;
                    foreach (var txStr in listMsg.Mempool)
                    {
                        var bytes = Base16.Decode(txStr);
                        var tx    = Transaction.Unserialize(bytes);
                        try
                        {
                            _mempool.Submit(tx);
                            submittedCount++;
                        }
                        catch
                        {
                        }

                        Logger.Message(submittedCount + " new transactions");
                    }
                }

                if (listMsg.Kind.HasFlag(RequestKind.Blocks))
                {
                    Chain chain = null;
                    foreach (var entry in listMsg.Blocks)
                    {
                        chain = Nexus.GetChainByName(entry.Key);
                        if (chain == null)
                        {
                            continue;
                        }

                        var blockRange = entry.Value;
                        foreach (var block in blockRange.blocks)
                        {
                            var transactions = new List <Transaction>();
                            foreach (var txHash in block.TransactionHashes)
                            {
                                var tx = entry.Value.transactions[txHash];
                                transactions.Add(tx);
                            }

                            if (block.Height > chain.Height)
                            {
                                var key = $"{chain.Name}.{block.Height}";
                                lock (_pendingBlocks)
                                {
                                    _pendingBlocks[key] = new PendingBlock(chain.Name, block, transactions);
                                }
                            }
                        }

                        _lastRequestTime = DateTime.UtcNow;
                        //Thread.Sleep(10000);
                    }
                }

                if (blockFetches.Count > 0)
                {
                    outKind |= RequestKind.Blocks;
                }

                if (outKind != RequestKind.None)
                {
                    var answer = new RequestMessage(this.Address, this.PublicEndpoint, outKind, Nexus.Name);

                    if (blockFetches.Count > 0)
                    {
                        answer.SetBlocks(blockFetches);
                    }

                    return(answer);
                }

                return(null);
            }

            case Opcode.MEMPOOL_Add:
            {
                if (Capabilities.HasFlag(PeerCaps.Mempool))
                {
                    var memtx           = (MempoolAddMessage)msg;
                    int submissionCount = 0;
                    foreach (var tx in memtx.Transactions)
                    {
                        try
                        {
                            if (_mempool.Submit(tx))
                            {
                                submissionCount++;
                            }
                        }
                        catch
                        {
                            // ignore
                        }
                    }

                    Logger.Message($"Added {submissionCount} txs to the mempool");
                }

                return(null);
            }

            case Opcode.BLOCKS_List:
            {
                break;
            }

            case Opcode.ERROR:
            {
                var errorMsg = (ErrorMessage)msg;
                if (string.IsNullOrEmpty(errorMsg.Text))
                {
                    Logger.Error($"ERROR: {errorMsg.Code}");
                }
                else
                {
                    Logger.Error($"ERROR: {errorMsg.Code} ({errorMsg.Text})");
                }

                return(null);
            }
            }

            throw new NodeException("No answer sent to request " + msg.Opcode);
        }
Beispiel #4
0
        private Message HandleMessage(Peer peer, Message msg)
        {
            if (msg.IsSigned && !msg.Address.IsNull)
            {
                if (msg.Address.IsUser)
                {
                    peer.SetAddress(msg.Address);
                }
                else
                {
                    return(new ErrorMessage(Address, P2PError.InvalidAddress));
                }
            }
            else
            {
                return(new ErrorMessage(Address, P2PError.MessageShouldBeSigned));
            }

            switch (msg.Opcode)
            {
            case Opcode.EVENT:
            {
                var evtMessage = (EventMessage)msg;
                var evt        = evtMessage.Event;
                Logger.Message("New event: " + evt.ToString());
                return(null);
            }

            case Opcode.REQUEST:
            {
                var request = (RequestMessage)msg;

                if (request.NexusName != Nexus.Name)
                {
                    return(new ErrorMessage(Address, P2PError.InvalidNexus));
                }

                if (request.Kind == RequestKind.None)
                {
                    return(null);
                }

                var answer = new ListMessage(this.Address, request.Kind);

                if (request.Kind.HasFlag(RequestKind.Peers))
                {
                    answer.SetPeers(this.Peers.Where(x => x != peer).Select(x => x.Endpoint));
                }

                if (request.Kind.HasFlag(RequestKind.Chains))
                {
                    var chainList = Nexus.GetChains(Nexus.RootStorage);
                    var chains    = chainList.Select(x => Nexus.GetChainByName(x)).Select(x => new ChainInfo(x.Name, Nexus.GetParentChainByName(x.Name), x.Height));
                    answer.SetChains(chains);
                }

                if (request.Kind.HasFlag(RequestKind.Mempool) && Capabilities.HasFlag(PeerCaps.Mempool))
                {
                    var txs = _mempool.GetTransactions().Select(x => Base16.Encode(x.ToByteArray(true)));
                    answer.SetMempool(txs);
                }

                if (request.Kind.HasFlag(RequestKind.Blocks))
                {
                    foreach (var entry in request.Blocks)
                    {
                        var chain = this.Nexus.GetChainByName(entry.Key);
                        if (chain == null)
                        {
                            continue;
                        }

                        var startBlock = entry.Value;
                        if (startBlock > chain.Height)
                        {
                            continue;
                        }

                        answer.AddBlockRange(chain, startBlock, 1);
                    }
                }

                return(answer);
            }

            case Opcode.LIST:
            {
                var listMsg = (ListMessage)msg;

                var outKind = RequestKind.None;

                if (listMsg.Kind.HasFlag(RequestKind.Peers))
                {
                    var newPeers = listMsg.Peers.Where(x => !IsKnown(x));
                    foreach (var entry in listMsg.Peers)
                    {
                        Logger.Message("New peer: " + entry.ToString());
                    }
                    QueueEndpoints(newPeers);
                }

                var blockFetches = new Dictionary <string, BigInteger>();
                if (listMsg.Kind.HasFlag(RequestKind.Chains))
                {
                    foreach (var entry in listMsg.Chains)
                    {
                        var chain = Nexus.GetChainByName(entry.name);
                        // NOTE if we dont find this chain then it is too soon for ask for blocks from that chain
                        if (chain != null && chain.Height < entry.height)
                        {
                            blockFetches[entry.name] = chain.Height + 1;
                        }
                    }
                }

                if (listMsg.Kind.HasFlag(RequestKind.Mempool) && Capabilities.HasFlag(PeerCaps.Mempool))
                {
                    int submittedCount = 0;
                    foreach (var txStr in listMsg.Mempool)
                    {
                        var bytes = Base16.Decode(txStr);
                        var tx    = Transaction.Unserialize(bytes);
                        try
                        {
                            _mempool.Submit(tx);
                            submittedCount++;
                        }
                        catch
                        {
                        }

                        Logger.Message(submittedCount + " new transactions");
                    }
                }

                if (listMsg.Kind.HasFlag(RequestKind.Blocks))
                {
                    bool addedBlocks = false;

                    Chain chain = null;
                    foreach (var entry in listMsg.Blocks)
                    {
                        chain = Nexus.GetChainByName(entry.Key);
                        if (chain == null)
                        {
                            continue;
                        }

                        var blockRange = entry.Value;
                        foreach (var block in blockRange.blocks)
                        {
                            var transactions = new List <Transaction>();
                            foreach (var txHash in block.TransactionHashes)
                            {
                                var tx = entry.Value.transactions[txHash];
                                transactions.Add(tx);
                            }

                            if (chain.Height + 1 < block.Height)
                            {
                                _blockCache.Add(block.Height, Tuple.Create(block, transactions));
                            }
                            else
                            {
                                addedBlocks = HandleBlock(chain, block, transactions);
                            }
                        }
                        //Thread.Sleep(10000);
                    }
                    // check if we have any cached blocks TODO: needs to be revisited when we have multiple chains

                    if (_blockCache.ContainsKey(chain.Height + 1))
                    {
                        foreach (var entry in _blockCache.OrderBy(x => x.Key))
                        {
                            if (entry.Key == chain.Height + 1)
                            {
                                addedBlocks = HandleBlock(chain, entry.Value.Item1 /*Block*/, entry.Value.Item2 /*Transactions*/);
                            }
                        }
                    }

                    if (addedBlocks)
                    {
                        outKind |= RequestKind.Chains;
                    }
                }

                if (blockFetches.Count > 0)
                {
                    outKind |= RequestKind.Blocks;
                }

                if (outKind != RequestKind.None)
                {
                    var answer = new RequestMessage(outKind, Nexus.Name, this.Address);

                    if (blockFetches.Count > 0)
                    {
                        answer.SetBlocks(blockFetches);
                    }

                    return(answer);
                }

                break;
            }

            case Opcode.MEMPOOL_Add:
            {
                if (Capabilities.HasFlag(PeerCaps.Mempool))
                {
                    var memtx           = (MempoolAddMessage)msg;
                    int submissionCount = 0;
                    foreach (var tx in memtx.Transactions)
                    {
                        try
                        {
                            if (_mempool.Submit(tx))
                            {
                                submissionCount++;
                            }
                        }
                        catch
                        {
                            // ignore
                        }
                    }

                    Logger.Message($"Added {submissionCount} txs to the mempool");
                }
                break;
            }

            case Opcode.BLOCKS_List:
            {
                break;
            }

            case Opcode.ERROR:
            {
                var errorMsg = (ErrorMessage)msg;
                if (string.IsNullOrEmpty(errorMsg.Text))
                {
                    Logger.Error($"ERROR: {errorMsg.Code}");
                }
                else
                {
                    Logger.Error($"ERROR: {errorMsg.Code} ({errorMsg.Text})");
                }
                break;
            }
            }

            Logger.Message("No answer sent.");
            return(null);
        }